diff --git a/.github/ISSUE_TEMPLATE/tracking_issue_future.md b/.github/ISSUE_TEMPLATE/tracking_issue_future.md new file mode 100644 index 000000000000..f04a458d8a5a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tracking_issue_future.md @@ -0,0 +1,54 @@ +--- +name: Future Incompatibility Tracking Issue +about: A tracking issue for a future-incompatible lint +title: Tracking Issue for future-incompatibility lint XXX +labels: C-tracking-issue C-future-incompatibility T-compiler A-lints +--- + + +This is the **tracking issue** for the `YOUR_LINT_NAME_HERE` future-compatibility warning and other related errors. The goal of this page is describe why this change was made and how you can fix code that is affected by it. It also provides a place to ask questions or register a complaint if you feel the change should not be made. For more information on the policy around future-compatibility warnings, see our [breaking change policy guidelines][guidelines]. + +[guidelines]: https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html + +### What is the warning for? + +*Describe the conditions that trigger the warning.* + +### Why was this change made? + +*Explain why this change was made. If there is additional context, like an MCP, link it here.* + +### Example + +```rust +// Include an example here. +``` + +### Recommendations + +*Give some recommendations on how a user can avoid the lint.* + +### When will this warning become a hard error? + +*If known, describe the future plans. For example, how long you anticipate this being a warning, or if there are other factors that will influence the anticipated closure.* + +### Steps + +- [ ] Implement the lint +- [ ] Raise lint level to deny +- [ ] Make lint report in dependencies +- [ ] Switch to a hard error + +### Implementation history + + diff --git a/.gitmodules b/.gitmodules index d09d81ccadcb..fbf2f59b38da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -45,7 +45,7 @@ shallow = true [submodule "src/tools/enzyme"] path = src/tools/enzyme - url = https://github.com/rust-lang/Enzyme.git + url = https://github.com/rust-lang/enzyme.git shallow = true [submodule "src/gcc"] path = src/gcc diff --git a/Cargo.lock b/Cargo.lock index f26d858b698d..59f7f3dda8fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,7 +216,7 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow 0.7.9", + "winnow 0.7.10", ] [[package]] @@ -487,9 +487,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -507,9 +507,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -537,7 +537,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy" -version = "0.1.88" +version = "0.1.89" dependencies = [ "anstream", "askama", @@ -569,7 +569,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.88" +version = "0.1.89" dependencies = [ "clippy_utils", "itertools", @@ -594,7 +594,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.88" +version = "0.1.89" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -624,7 +624,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.88" +version = "0.1.89" dependencies = [ "arrayvec", "itertools", @@ -738,7 +738,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows 0.59.0", + "windows", ] [[package]] @@ -977,9 +977,9 @@ dependencies = [ [[package]] name = "derive_setters" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +checksum = "d9c848e86c87e5cc305313041c5677d4d95d60baa71cf95e5f6ea2554bb629ff" dependencies = [ "darling", "proc-macro2", @@ -1437,9 +1437,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -1587,7 +1587,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core", ] [[package]] @@ -1601,14 +1601,15 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", - "yoke", + "potential_utf", + "yoke 0.8.0", "zerofrom", - "zerovec", + "zerovec 0.11.2", ] [[package]] @@ -1620,9 +1621,9 @@ dependencies = [ "displaydoc", "icu_list_data", "icu_locid_transform", - "icu_provider", + "icu_provider 1.5.0", "regex-automata 0.2.0", - "writeable", + "writeable 0.5.5", ] [[package]] @@ -1631,6 +1632,19 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52b1a7fbdbf3958f1be8354cb59ac73f165b7b7082d447ff2090355c9a069120" +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap 0.8.0", + "tinystr 0.8.1", + "writeable 0.6.1", + "zerovec 0.11.2", +] + [[package]] name = "icu_locid" version = "1.5.0" @@ -1638,10 +1652,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "litemap 0.7.5", + "tinystr 0.7.6", + "writeable 0.5.5", + "zerovec 0.10.4", ] [[package]] @@ -1653,9 +1667,9 @@ dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] @@ -1666,48 +1680,46 @@ checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", - "icu_provider", + "icu_provider 2.0.0", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", + "zerovec 0.11.2", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 2.0.0", + "potential_utf", + "zerotrie", + "zerovec 0.11.2", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" @@ -1719,11 +1731,28 @@ dependencies = [ "icu_locid", "icu_provider_macros", "stable_deref_trait", - "tinystr", - "writeable", - "yoke", + "tinystr 0.7.6", + "writeable 0.5.5", + "yoke 0.7.5", "zerofrom", - "zerovec", + "zerovec 0.10.4", +] + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr 0.8.1", + "writeable 0.6.1", + "yoke 0.8.0", + "zerofrom", + "zerotrie", + "zerovec 0.11.2", ] [[package]] @@ -1734,9 +1763,9 @@ checksum = "d6324dfd08348a8e0374a447ebd334044d766b1839bb8d5ccf2482a99a77c0bc" dependencies = [ "icu_locid", "icu_locid_transform", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] @@ -1775,9 +1804,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1905,9 +1934,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -1918,9 +1947,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -1933,7 +1962,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -2033,9 +2062,9 @@ dependencies = [ [[package]] name = "libffi" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9434b6fc77375fb624698d5f8c49d7e80b10d59eb1219afda27d1f824d4074" +checksum = "ebfd30a67b482a08116e753d0656cb626548cf4242543e5cc005be7639d99838" dependencies = [ "libc", "libffi-sys", @@ -2043,9 +2072,9 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ead36a2496acfc8edd6cc32352110e9478ac5b9b5f5b9856ebd3d28019addb84" +checksum = "f003aa318c9f0ee69eb0ada7c78f5c9d2fedd2ceb274173b5c7ff475eee584a3" dependencies = [ "cc", ] @@ -2062,9 +2091,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -2118,6 +2147,12 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lld-wrapper" version = "0.1.0" @@ -2279,7 +2314,7 @@ dependencies = [ "chrono-tz", "colored", "directories", - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", "libffi", "libloading", @@ -2769,6 +2804,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec 0.11.2", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2919,7 +2963,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -3074,9 +3118,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d984a9db43148467059309bd1e5ad577085162f695d9fe2cf3543aeb25cd38" +checksum = "fb332121f7845c6bd016f9655cf22f03c2999df936694b624a88669a78667d98" dependencies = [ "anyhow", "rustc_version", @@ -3309,8 +3353,8 @@ dependencies = [ "icu_list", "icu_locid", "icu_locid_transform", - "icu_provider", - "zerovec", + "icu_provider 1.5.0", + "zerovec 0.10.4", ] [[package]] @@ -3449,7 +3493,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.219.2", - "windows 0.59.0", + "windows", ] [[package]] @@ -3508,7 +3552,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3571,7 +3615,7 @@ dependencies = [ "shlex", "stable_mir", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3626,7 +3670,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -4371,7 +4415,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -5058,7 +5102,7 @@ dependencies = [ "libc", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -5084,12 +5128,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5264,7 +5308,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", - "zerovec", + "zerovec 0.10.4", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec 0.11.2", ] [[package]] @@ -5284,9 +5338,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -5490,9 +5544,9 @@ dependencies = [ [[package]] name = "unic-langid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" dependencies = [ "unic-langid-impl", "unic-langid-macros", @@ -5500,30 +5554,30 @@ dependencies = [ [[package]] name = "unic-langid-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" dependencies = [ - "tinystr", + "tinystr 0.8.1", ] [[package]] name = "unic-langid-macros" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e" +checksum = "d5957eb82e346d7add14182a3315a7e298f04e1ba4baac36f7f0dbfedba5fc25" dependencies = [ "proc-macro-hack", - "tinystr", + "tinystr 0.8.1", "unic-langid-impl", "unic-langid-macros-impl", ] [[package]] name = "unic-langid-macros-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" +checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", @@ -5639,12 +5693,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -5669,7 +5717,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -5826,6 +5874,16 @@ dependencies = [ "wasmparser 0.229.0", ] +[[package]] +name = "wasm-encoder" +version = "0.230.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" +dependencies = [ + "leb128fmt", + "wasmparser 0.230.0", +] + [[package]] name = "wasm-metadata" version = "0.229.0" @@ -5871,23 +5929,34 @@ dependencies = [ ] [[package]] -name = "wast" -version = "229.0.0" +name = "wasmparser" +version = "0.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63fcaff613c12225696bb163f79ca38ffb40e9300eff0ff4b8aa8b2f7eadf0d9" +checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wast" +version = "230.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8edac03c5fa691551531533928443faf3dc61a44f814a235c7ec5d17b7b34f1" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.229.0", + "wasm-encoder 0.230.0", ] [[package]] name = "wat" -version = "1.229.0" +version = "1.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4189bad08b70455a9e9e67dc126d2dcf91fac143a80f1046747a5dde6d4c33e0" +checksum = "0d77d62229e38db83eac32bacb5f61ebb952366ab0dae90cf2b3c07a65eea894" dependencies = [ "wast", ] @@ -5933,16 +6002,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" -dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", -] - [[package]] name = "windows" version = "0.61.1" @@ -5950,7 +6009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -5973,20 +6032,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" -dependencies = [ - "windows-implement 0.59.0", - "windows-interface", - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core", ] [[package]] @@ -5995,11 +6041,11 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", + "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -6008,21 +6054,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -6057,7 +6092,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] @@ -6070,15 +6105,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.0" @@ -6311,9 +6337,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -6370,18 +6396,18 @@ dependencies = [ "wasmparser 0.229.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "x" version = "0.1.1" @@ -6422,7 +6448,19 @@ checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive", + "yoke-derive 0.7.5", + "zerofrom", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.8.0", "zerofrom", ] @@ -6438,6 +6476,18 @@ dependencies = [ "synstructure", ] +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.25" @@ -6479,15 +6529,37 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke 0.8.0", + "zerofrom", +] + [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ - "yoke", + "yoke 0.7.5", "zerofrom", - "zerovec-derive", + "zerovec-derive 0.10.3", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke 0.8.0", + "zerofrom", + "zerovec-derive 0.11.1", ] [[package]] @@ -6500,3 +6572,14 @@ dependencies = [ "quote", "syn 2.0.101", ] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 4ace80a73444..a16219361c05 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -610,7 +610,7 @@ impl Pat { /// Walk top-down and call `it` in each place where a pattern occurs /// starting with the root pattern `walk` is called on. If `it` returns /// false then we will descend no further but siblings will be processed. - pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) { + pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) { if !it(self) { return; } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 7718644b9a9a..512288a0f7d8 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -621,13 +621,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { &ocx, op, span, ) { Ok(_) => ocx.select_all_or_error(), - Err(e) => { - if e.is_empty() { - ocx.select_all_or_error() - } else { - e - } - } + Err(e) => e, }; // Could have no errors if a type lowering error, say, caused the query diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 3e8ddb8abd43..62ee71fecc27 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -18,7 +18,31 @@ use {rustc_ast as ast, rustc_parse_format as parse}; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; -pub struct AsmArgs { +/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise +/// not validated at all. +pub struct AsmArg { + pub kind: AsmArgKind, + pub span: Span, +} + +pub enum AsmArgKind { + Template(P), + Operand(Option, ast::InlineAsmOperand), + Options(Vec), + ClobberAbi(Vec<(Symbol, Span)>), +} + +pub struct AsmOption { + pub symbol: Symbol, + pub span: Span, + // A bitset, with only the bit for this option's symbol set. + pub options: ast::InlineAsmOptions, + // Used when suggesting to remove an option. + pub span_with_comma: Span, +} + +/// Validated assembly arguments, ready for macro expansion. +struct ValidatedAsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxIndexMap, @@ -59,41 +83,95 @@ fn eat_operand_keyword<'a>( } } -fn parse_args<'a>( - ecx: &ExtCtxt<'a>, - sp: Span, - tts: TokenStream, +fn parse_asm_operand<'a>( + p: &mut Parser<'a>, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let mut p = ecx.new_parser_from_tts(tts); - parse_asm_args(&mut p, sp, asm_macro) +) -> PResult<'a, Option> { + let dcx = p.dcx(); + + Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + ast::InlineAsmOperand::In { reg, expr } + } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: false } + } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: true } + } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: false } + } + } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: true } + } + } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } + } else if p.eat_keyword(exp!(Const)) { + let anon_const = p.parse_expr_anon_const()?; + ast::InlineAsmOperand::Const { anon_const } + } else if p.eat_keyword(exp!(Sym)) { + let expr = p.parse_expr()?; + let ast::ExprKind::Path(qself, path) = &expr.kind else { + let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); + return Err(err); + }; + let sym = + ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; + ast::InlineAsmOperand::Sym { sym } + } else { + return Ok(None); + })) } -// Primarily public for rustfmt consumption. -// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` +// Public for rustfmt. pub fn parse_asm_args<'a>( p: &mut Parser<'a>, sp: Span, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { +) -> PResult<'a, Vec> { let dcx = p.dcx(); if p.token == token::Eof { return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); } + let mut args = Vec::new(); + let first_template = p.parse_expr()?; - let mut args = AsmArgs { - templates: vec![first_template], - operands: vec![], - named_args: Default::default(), - reg_args: Default::default(), - clobber_abis: Vec::new(), - options: ast::InlineAsmOptions::empty(), - options_spans: vec![], - }; + args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) }); let mut allow_templates = true; + while p.token != token::Eof { if !p.eat(exp!(Comma)) { if allow_templates { @@ -104,27 +182,39 @@ pub fn parse_asm_args<'a>( return Err(p.expect(exp!(Comma)).err().unwrap()); } } + + // Accept trailing commas. if p.token == token::Eof { break; - } // accept trailing commas - - // Parse clobber_abi - if p.eat_keyword(exp!(ClobberAbi)) { - parse_clobber_abi(p, &mut args)?; - allow_templates = false; - continue; - } - - // Parse options - if p.eat_keyword(exp!(Options)) { - parse_options(p, &mut args, asm_macro)?; - allow_templates = false; - continue; } let span_start = p.token.span; - // Parse operand names + // Parse `clobber_abi`. + if p.eat_keyword(exp!(ClobberAbi)) { + allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + span: span_start.to(p.prev_token.span), + }); + + continue; + } + + // Parse `options`. + if p.eat_keyword(exp!(Options)) { + allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::Options(parse_options(p, asm_macro)?), + span: span_start.to(p.prev_token.span), + }); + + continue; + } + + // Parse operand names. let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { let (ident, _) = p.token.ident().unwrap(); p.bump(); @@ -135,69 +225,13 @@ pub fn parse_asm_args<'a>( None }; - let mut explicit_reg = false; - let op = if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: false } - } - } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: true } - } - } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { - let block = p.parse_block()?; - ast::InlineAsmOperand::Label { block } - } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const()?; - ast::InlineAsmOperand::Const { anon_const } - } else if p.eat_keyword(exp!(Sym)) { - let expr = p.parse_expr()?; - let ast::ExprKind::Path(qself, path) = &expr.kind else { - let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); - return Err(err); - }; - let sym = ast::InlineAsmSym { - id: ast::DUMMY_NODE_ID, - qself: qself.clone(), - path: path.clone(), - }; - ast::InlineAsmOperand::Sym { sym } + if let Some(op) = parse_asm_operand(p, asm_macro)? { + allow_templates = false; + + args.push(AsmArg { + span: span_start.to(p.prev_token.span), + kind: AsmArgKind::Operand(name, op), + }); } else if allow_templates { let template = p.parse_expr()?; // If it can't possibly expand to a string, provide diagnostics here to include other @@ -217,55 +251,164 @@ pub fn parse_asm_args<'a>( return Err(err); } } - args.templates.push(template); - continue; + + args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) }); } else { p.unexpected_any()? - }; - - allow_templates = false; - let span = span_start.to(p.prev_token.span); - let slot = args.operands.len(); - args.operands.push((op, span)); - - // Validate the order of named, positional & explicit register operands and - // clobber_abi/options. We do this at the end once we have the full span - // of the argument available. - if explicit_reg { - if name.is_some() { - dcx.emit_err(errors::AsmExplicitRegisterName { span }); - } - args.reg_args.insert(slot); - } else if let Some(name) = name { - if let Some(&prev) = args.named_args.get(&name) { - dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 }); - continue; - } - args.named_args.insert(name, slot); - } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); - let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); - - dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); } } - if args.options.contains(ast::InlineAsmOptions::NOMEM) - && args.options.contains(ast::InlineAsmOptions::READONLY) + Ok(args) +} + +fn parse_args<'a>( + ecx: &ExtCtxt<'a>, + sp: Span, + tts: TokenStream, + asm_macro: AsmMacro, +) -> PResult<'a, ValidatedAsmArgs> { + let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; + validate_asm_args(ecx, asm_macro, args) +} + +fn validate_asm_args<'a>( + ecx: &ExtCtxt<'a>, + asm_macro: AsmMacro, + args: Vec, +) -> PResult<'a, ValidatedAsmArgs> { + let dcx = ecx.dcx(); + + let mut validated = ValidatedAsmArgs { + templates: vec![], + operands: vec![], + named_args: Default::default(), + reg_args: Default::default(), + clobber_abis: Vec::new(), + options: ast::InlineAsmOptions::empty(), + options_spans: vec![], + }; + + let mut allow_templates = true; + + for arg in args { + match arg.kind { + AsmArgKind::Template(template) => { + // The error for the first template is delayed. + if !allow_templates { + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } + } + } + + validated.templates.push(template); + } + AsmArgKind::Operand(name, op) => { + allow_templates = false; + + let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + let span = arg.span; + let slot = validated.operands.len(); + validated.operands.push((op, span)); + + // Validate the order of named, positional & explicit register operands and + // clobber_abi/options. We do this at the end once we have the full span + // of the argument available. + + if explicit_reg { + if name.is_some() { + dcx.emit_err(errors::AsmExplicitRegisterName { span }); + } + validated.reg_args.insert(slot); + } else if let Some(name) = name { + if let Some(&prev) = validated.named_args.get(&name) { + dcx.emit_err(errors::AsmDuplicateArg { + span, + name, + prev: validated.operands[prev].1, + }); + continue; + } + validated.named_args.insert(name, slot); + } else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() { + let named = + validated.named_args.values().map(|p| validated.operands[*p].1).collect(); + let explicit = + validated.reg_args.iter().map(|p| validated.operands[p].1).collect(); + + dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + } + } + AsmArgKind::Options(new_options) => { + allow_templates = false; + + for asm_option in new_options { + let AsmOption { span, symbol, span_with_comma, options } = asm_option; + + if !asm_macro.is_supported_option(options) { + // Tool-only output. + dcx.emit_err(errors::AsmUnsupportedOption { + span, + symbol, + span_with_comma, + macro_name: asm_macro.macro_name(), + }); + } else if validated.options.contains(options) { + // Tool-only output. + dcx.emit_err(errors::AsmOptAlreadyprovided { + span, + symbol, + span_with_comma, + }); + } else { + validated.options |= asm_option.options; + } + } + + validated.options_spans.push(arg.span); + } + AsmArgKind::ClobberAbi(new_abis) => { + allow_templates = false; + + match &new_abis[..] { + // This should have errored above during parsing. + [] => unreachable!(), + [(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)), + _ => validated.clobber_abis.extend(new_abis), + } + } + } + } + + if validated.options.contains(ast::InlineAsmOptions::NOMEM) + && validated.options.contains(ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && validated.options.contains(ast::InlineAsmOptions::NORETURN) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && !validated + .options + .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmPureCombine { spans }); } @@ -273,7 +416,7 @@ pub fn parse_asm_args<'a>( let mut outputs_sp = vec![]; let mut regclass_outputs = vec![]; let mut labels_sp = vec![]; - for (op, op_sp) in &args.operands { + for (op, op_sp) in &validated.operands { match op { ast::InlineAsmOperand::Out { reg, expr, .. } | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => { @@ -296,10 +439,10 @@ pub fn parse_asm_args<'a>( _ => {} } } - if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() }); + if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { + dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() }); } - if args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() && labels_sp.is_empty() { @@ -307,15 +450,15 @@ pub fn parse_asm_args<'a>( // Bail out now since this is likely to confuse MIR return Err(err); } - if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { + if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { dcx.emit_err(errors::AsmMayUnwind { labels_sp }); } - if !args.clobber_abis.is_empty() { + if !validated.clobber_abis.is_empty() { match asm_macro { AsmMacro::GlobalAsm | AsmMacro::NakedAsm => { let err = dcx.create_err(errors::AsmUnsupportedClobberAbi { - spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), macro_name: asm_macro.macro_name(), }); @@ -326,71 +469,21 @@ pub fn parse_asm_args<'a>( if !regclass_outputs.is_empty() { dcx.emit_err(errors::AsmClobberNoReg { spans: regclass_outputs, - clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), }); } } } } - Ok(args) + Ok(validated) } -/// Report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); -} - -/// Report an invalid option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmUnsupportedOption { - span, - symbol, - full_span, - macro_name: asm_macro.macro_name(), - }); -} - -/// Try to set the provided option in the provided `AsmArgs`. -/// If it is already set, report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the error will not point to the correct spot. -fn try_set_option<'a>( - p: &Parser<'a>, - args: &mut AsmArgs, - asm_macro: AsmMacro, - symbol: Symbol, - option: ast::InlineAsmOptions, -) { - if !asm_macro.is_supported_option(option) { - err_unsupported_option(p, asm_macro, symbol, p.prev_token.span); - } else if args.options.contains(option) { - err_duplicate_option(p, symbol, p.prev_token.span); - } else { - args.options |= option; - } -} - -fn parse_options<'a>( - p: &mut Parser<'a>, - args: &mut AsmArgs, - asm_macro: AsmMacro, -) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { p.expect(exp!(OpenParen))?; + let mut asm_options = Vec::new(); + while !p.eat(exp!(CloseParen)) { const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ (exp!(Pure), ast::InlineAsmOptions::PURE), @@ -405,38 +498,38 @@ fn parse_options<'a>( ]; 'blk: { - for (exp, option) in OPTIONS { - let kw_matched = if asm_macro.is_supported_option(option) { + for (exp, options) in OPTIONS { + // Gives a more accurate list of expected next tokens. + let kw_matched = if asm_macro.is_supported_option(options) { p.eat_keyword(exp) } else { p.eat_keyword_noexpect(exp.kw) }; if kw_matched { - try_set_option(p, args, asm_macro, exp.kw, option); + let span = p.prev_token.span; + let span_with_comma = + if p.token == token::Comma { span.to(p.token.span) } else { span }; + + asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); break 'blk; } } - return p.unexpected(); + return p.unexpected_any(); } - // Allow trailing commas + // Allow trailing commas. if p.eat(exp!(CloseParen)) { break; } p.expect(exp!(Comma))?; } - let new_span = span_start.to(p.prev_token.span); - args.options_spans.push(new_span); - - Ok(()) + Ok(asm_options) } -fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { p.expect(exp!(OpenParen))?; if p.eat(exp!(CloseParen)) { @@ -462,31 +555,14 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, p.expect(exp!(Comma))?; } - let full_span = span_start.to(p.prev_token.span); - - match &new_abis[..] { - // should have errored above during parsing - [] => unreachable!(), - [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), - abis => { - for (abi, span) in abis { - args.clobber_abis.push((*abi, *span)); - } - } - } - - Ok(()) + Ok(new_abis) } -fn parse_reg<'a>( - p: &mut Parser<'a>, - explicit_reg: &mut bool, -) -> PResult<'a, ast::InlineAsmRegOrRegClass> { +fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { p.expect(exp!(OpenParen))?; let result = match p.token.uninterpolate().kind { token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { - *explicit_reg = true; ast::InlineAsmRegOrRegClass::Reg(symbol) } _ => { @@ -503,7 +579,7 @@ fn parse_reg<'a>( fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, asm_macro: AsmMacro, - args: AsmArgs, + args: ValidatedAsmArgs, ) -> ExpandResult, ()> { let mut template = vec![]; // Register operands are implicitly used since they are not allowed to be diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d14ad8f40144..b28f7d312d93 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -910,7 +910,7 @@ pub(crate) struct AsmOptAlreadyprovided { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, } #[derive(Diagnostic)] @@ -921,7 +921,7 @@ pub(crate) struct AsmUnsupportedOption { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, pub(crate) macro_name: &'static str, } diff --git a/compiler/rustc_codegen_gcc/.gitattributes b/compiler/rustc_codegen_gcc/.gitattributes new file mode 100644 index 000000000000..b9cd1111c8d0 --- /dev/null +++ b/compiler/rustc_codegen_gcc/.gitattributes @@ -0,0 +1 @@ +Cargo.lock linguist-generated=false \ No newline at end of file diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index 4b9f48e7b183..f26ac3b755fb 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -101,9 +101,8 @@ jobs: if: ${{ matrix.cargo_runner }} run: | # FIXME: these tests fail when the sysroot is compiled with LTO because of a missing symbol in proc-macro. - # TODO: remove --skip test_mm512_stream_ps when stdarch is updated in rustc. # TODO: remove --skip test_tile_ when it's implemented. - STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features --cfg stdarch_intel_sde" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_mm512_stream_ps --skip test_tile_ + STDARCH_TEST_SKIP_FUNCTION="xsave,xsaveopt,xsave64,xsaveopt64" STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_tile_ # Summary job for the merge queue. # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! diff --git a/compiler/rustc_codegen_gcc/CONTRIBUTING.md b/compiler/rustc_codegen_gcc/CONTRIBUTING.md new file mode 100644 index 000000000000..8e313ab08b59 --- /dev/null +++ b/compiler/rustc_codegen_gcc/CONTRIBUTING.md @@ -0,0 +1,101 @@ +# Contributing to rustc_codegen_gcc + +Welcome to the `rustc_codegen_gcc` project! This guide will help you get started as a contributor. The project aims to provide a GCC codegen backend for rustc, allowing Rust compilation on platforms unsupported by LLVM and potentially improving runtime performance through GCC's optimizations. + +## Getting Started + +### Setting Up Your Development Environment + +For detailed setup instructions including dependencies, build steps, and initial testing, please refer to our [README](Readme.md). The README contains the most up-to-date information on: + +- Required dependencies and system packages +- Repository setup and configuration +- Build process +- Basic test verification + +Once you've completed the setup process outlined in the README, you can proceed with the contributor-specific information below. + +## Communication Channels + +- Matrix: Join our [Matrix channel](https://matrix.to/#/#rustc_codegen_gcc:matrix.org) +- IRC: Join us on [IRC](https://web.libera.chat/#rustc_codegen_gcc) +- [GitHub Issues](https://github.com/rust-lang/rustc_codegen_gcc/issues): For bug reports and feature discussions + +We encourage new contributors to join our communication channels and introduce themselves. Feel free to ask questions about where to start or discuss potential contributions. + +## Understanding Core Concepts + +### Common Development Tasks + +#### Running Specific Tests + +To run specific tests, use appropriate flags such as: + +- `./y.sh test --test-libcore` +- `./y.sh test --std-tests` +- `cargo test -- ` + +Additionally, you can run the tests of `libgccjit`: + +```bash +# libgccjit tests +cd gcc-build/gcc +make check-jit +# For a specific test: +make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-asm.cc" +``` + +#### Debugging Tools + +The project provides several environment variables for debugging: + +- `CG_GCCJIT_DUMP_GIMPLE`: Dumps the GIMPLE IR +- `CG_RUSTFLAGS`: Additional Rust flags +- `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module +- `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation + +Full list of debugging options can be found in the [README](Readme.md#env-vars). + +## Making Contributions + +### Finding Issues to Work On + +1. Look for issues labeled with [`good first issue`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"good%20first%20issue") or [`help wanted`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"help%20wanted") +2. Check the [progress report](https://blog.antoyo.xyz/rustc_codegen_gcc-progress-report-34#state_of_rustc_codegen_gcc) for larger initiatives +3. Consider improving documentation or investigating [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests) (except `failing-ui-tests12.txt`) + +### Pull Request Process + +1. Fork the repository and create a new branch +2. Make your changes with clear commit messages +3. Add tests for new functionality +4. Update documentation as needed +5. Submit a PR with a description of your changes + +### Code Style Guidelines + +- Follow Rust standard coding conventions +- Ensure your code passes `rustfmt` and `clippy` +- Add comments explaining complex logic, especially in GCC interface code + +## Additional Resources + +- [Rustc Dev Guide](https://rustc-dev-guide.rust-lang.org/) +- [GCC Internals Documentation](https://gcc.gnu.org/onlinedocs/gccint/) +- Project-specific documentation in the `doc/` directory: + - [Common errors](doc/errors.md) + - [Debugging](doc/debugging.md) + - [Debugging libgccjit](doc/debugging-libgccjit.md) + - [Git subtree sync](doc/subtree.md) + - [List of useful commands](doc/tips.md) + - [Send a patch to GCC](doc/sending-gcc-patch.md) + +## Getting Help + +If you're stuck or unsure about anything: +1. Check the existing documentation in the `doc/` directory +2. Ask in the IRC or Matrix channels +3. Open a GitHub issue for technical problems +4. Comment on the issue you're working on if you need guidance + +Remember that all contributions, including documentation improvements, bug reports, and feature requests, are valuable to the project. diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index 832603aa7925..967a51a1cc64 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2895ddec764de7ac76fe6c056050c4801a80109c066f177a00a9cc8dee02b29b" +checksum = "ae99a89184220d967dd300139f2d2ae7d52c1a69d632b24aacc57c54625254ce" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac133db68db8a6a8b2c51ef4b18d8ea16682d5814c4641272fe37bbbc223d5f3" +checksum = "24edb7bfe2b7b27c6d09ed23eebfcab0b359c8fe978433f902943e6f127a0f1b" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index b50f2a626d57..c692a90f0a4a 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -2,7 +2,7 @@ name = "rustc_codegen_gcc" version = "0.1.0" authors = ["Antoni Boucher "] -edition = "2018" +edition = "2024" license = "MIT OR Apache-2.0" [lib] @@ -22,7 +22,7 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = "2.5" +gccjit = "2.7" #gccjit = { git = "https://github.com/rust-lang/gccjit.rs" } # Local copy. diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md index d0e4dbba6d35..859bb1568f4e 100644 --- a/compiler/rustc_codegen_gcc/Readme.md +++ b/compiler/rustc_codegen_gcc/Readme.md @@ -12,22 +12,38 @@ This is a GCC codegen for rustc, which means it can be loaded by the existing ru The primary goal of this project is to be able to compile Rust code on platforms unsupported by LLVM. A secondary goal is to check if using the gcc backend will provide any run-time speed improvement for the programs compiled using rustc. +## Getting Started + +Note: **This requires a patched libgccjit in order to work. +You need to use my [fork of gcc](https://github.com/rust-lang/gcc) which already includes these patches.** +The default configuration (see below in the [Quick start](#quick-start) section) will download a `libgccjit` built in the CI that already contains these patches, so you don't need to build this fork yourself if you use the default configuration. + ### Dependencies -**rustup:** Follow the instructions on the official [website](https://www.rust-lang.org/tools/install) +- rustup: follow instructions on the [official website](https://rustup.rs) +- consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) +- additional packages: `flex`, `libmpfr-dev`, `libgmp-dev`, `libmpc3`, `libmpc-dev` + +### Quick start -**DejaGnu:** Consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) +1. Clone and configure the repository: + ```bash + git clone https://github.com/rust-lang/rustc_codegen_gcc + cd rustc_codegen_gcc + cp config.example.toml config.toml + ``` - - -## Building - -**This requires a patched libgccjit in order to work. -You need to use my [fork of gcc](https://github.com/rust-lang/gcc) which already includes these patches.** - -```bash -$ cp config.example.toml config.toml -``` +2. Build and test: + ```bash + ./y.sh prepare # downloads and patches sysroot + ./y.sh build --sysroot --release + + # Verify setup with a simple test + ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml + + # Run full test suite (expect ~100 failing UI tests) + ./y.sh test --release + ``` If don't need to test GCC patches you wrote in our GCC fork, then the default configuration should be all you need. You can update the `rustc_codegen_gcc` without worrying about GCC. @@ -143,7 +159,7 @@ You can do the same manually (although we don't recommend it): $ LIBRARY_PATH="[gcc-path value]" LD_LIBRARY_PATH="[gcc-path value]" rustc +$(cat $CG_GCCJIT_DIR/rust-toolchain | grep 'channel' | cut -d '=' -f 2 | sed 's/"//g' | sed 's/ //g') -Cpanic=abort -Zcodegen-backend=$CG_GCCJIT_DIR/target/release/librustc_codegen_gcc.so --sysroot $CG_GCCJIT_DIR/build_sysroot/sysroot my_crate.rs ``` -## Env vars +## Environment variables * _**CG_GCCJIT_DUMP_ALL_MODULES**_: Enables dumping of all compilation modules. When set to "1", a dump is created for each module during compilation and stored in `/tmp/reproducers/`. * _**CG_GCCJIT_DUMP_MODULE**_: Enables dumping of a specific module. When set with the module name, e.g., `CG_GCCJIT_DUMP_MODULE=module_name`, a dump of that specific module is created in `/tmp/reproducers/`. diff --git a/compiler/rustc_codegen_gcc/build_system/Cargo.toml b/compiler/rustc_codegen_gcc/build_system/Cargo.toml index d2600ed5a031..540d82369fdf 100644 --- a/compiler/rustc_codegen_gcc/build_system/Cargo.toml +++ b/compiler/rustc_codegen_gcc/build_system/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "y" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] boml = "0.3.1" diff --git a/compiler/rustc_codegen_gcc/build_system/src/main.rs b/compiler/rustc_codegen_gcc/build_system/src/main.rs index 393617183061..c70b00e09ae7 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/main.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/main.rs @@ -60,7 +60,9 @@ pub enum Command { fn main() { if env::var("RUST_BACKTRACE").is_err() { - env::set_var("RUST_BACKTRACE", "1"); + unsafe { + env::set_var("RUST_BACKTRACE", "1"); + } } let command = match env::args().nth(1).as_deref() { diff --git a/compiler/rustc_codegen_gcc/build_system/src/utils.rs b/compiler/rustc_codegen_gcc/build_system/src/utils.rs index 401c23948e5d..ca177a5feb86 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/utils.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/utils.rs @@ -10,7 +10,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus, Output}; #[cfg(unix)] -extern "C" { +unsafe extern "C" { fn raise(signal: c_int) -> c_int; } diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index c554a87b8256..d1d8e8fd5bc4 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -1,6 +1,14 @@ #![feature( - no_core, lang_items, intrinsics, unboxed_closures, extern_types, - decl_macro, rustc_attrs, transparent_unions, auto_traits, freeze_impls, + no_core, + lang_items, + intrinsics, + unboxed_closures, + extern_types, + decl_macro, + rustc_attrs, + transparent_unions, + auto_traits, + freeze_impls, thread_local )] #![no_core] @@ -35,13 +43,13 @@ impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} pub trait DispatchFromDyn {} // &T -> &U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} // &mut T -> &mut U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} // *const T -> *const U -impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} // *mut T -> *mut U -impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} +impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} impl, U: ?Sized> DispatchFromDyn> for Box {} #[lang = "legacy_receiver"] @@ -52,8 +60,7 @@ impl LegacyReceiver for &mut T {} impl LegacyReceiver for Box {} #[lang = "receiver"] -trait Receiver { -} +trait Receiver {} #[lang = "copy"] pub trait Copy {} @@ -67,10 +74,13 @@ impl Copy for u16 {} impl Copy for u32 {} impl Copy for u64 {} impl Copy for usize {} +impl Copy for u128 {} impl Copy for i8 {} impl Copy for i16 {} impl Copy for i32 {} +impl Copy for i64 {} impl Copy for isize {} +impl Copy for i128 {} impl Copy for f32 {} impl Copy for f64 {} impl Copy for char {} @@ -336,7 +346,6 @@ impl PartialEq for u32 { } } - impl PartialEq for u64 { fn eq(&self, other: &u64) -> bool { (*self) == (*other) @@ -523,7 +532,11 @@ fn panic_in_cleanup() -> ! { #[track_caller] fn panic_bounds_check(index: usize, len: usize) -> ! { unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); + libc::printf( + "index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, + len, + index, + ); intrinsics::abort(); } } @@ -551,8 +564,7 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -pub trait Allocator { -} +pub trait Allocator {} impl Allocator for () {} @@ -634,6 +646,8 @@ pub union MaybeUninit { } pub mod intrinsics { + #[rustc_intrinsic] + pub const fn black_box(_dummy: T) -> T; #[rustc_intrinsic] pub fn abort() -> !; #[rustc_intrinsic] @@ -711,19 +725,27 @@ pub struct VaList<'a>(&'a mut VaListImpl); #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro stringify($($t:tt)*) { /* compiler built-in */ } +pub macro stringify($($t:tt)*) { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro file() { /* compiler built-in */ } +pub macro file() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro line() { /* compiler built-in */ } +pub macro line() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro cfg() { /* compiler built-in */ } +pub macro cfg() { + /* compiler built-in */ +} pub static A_STATIC: u8 = 42; diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index 125b04004b07..f62154968d3d 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -0ea98a1365b81f7488073512c850e8ee951a4afd +04ce66d8c918de9273bd7101638ad8724edf5e21 diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 452d3f22dc51..a8cda28688c8 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-04-25" +channel = "nightly-2025-05-12" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index a96b18e01c08..d882d3eecf49 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -9,9 +9,9 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::config; -#[cfg(feature = "master")] -use rustc_target::callconv::Conv; use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; +#[cfg(feature = "master")] +use rustc_target::callconv::{Conv, RiscvInterruptKind}; use crate::builder::Builder; use crate::context::CodegenCx; @@ -240,38 +240,57 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { #[cfg(feature = "master")] pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option> { - // TODO: handle the calling conventions returning None. let attribute = match conv { - Conv::C - | Conv::Rust - | Conv::CCmseNonSecureCall - | Conv::CCmseNonSecureEntry - | Conv::RiscvInterrupt { .. } => return None, - Conv::Cold => return None, + Conv::C | Conv::Rust => return None, + Conv::CCmseNonSecureCall => { + if arch == "arm" { + FnAttribute::ArmCmseNonsecureCall + } else { + return None; + } + } + Conv::CCmseNonSecureEntry => { + if arch == "arm" { + FnAttribute::ArmCmseNonsecureEntry + } else { + return None; + } + } + Conv::Cold => FnAttribute::Cold, + // NOTE: the preserve attributes are not yet implemented in GCC: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899 Conv::PreserveMost => return None, Conv::PreserveAll => return None, Conv::GpuKernel => { - // TODO(antoyo): remove clippy allow attribute when this is implemented. - #[allow(clippy::if_same_then_else)] if arch == "amdgpu" { - return None; + FnAttribute::GcnAmdGpuHsaKernel } else if arch == "nvptx64" { - return None; + FnAttribute::NvptxKernel } else { panic!("Architecture {} does not support GpuKernel calling convention", arch); } } - Conv::AvrInterrupt => return None, - Conv::AvrNonBlockingInterrupt => return None, - Conv::ArmAapcs => return None, - Conv::Msp430Intr => return None, - Conv::X86Fastcall => return None, - Conv::X86Intr => return None, - Conv::X86Stdcall => return None, - Conv::X86ThisCall => return None, + // TODO(antoyo): check if those AVR attributes are mapped correctly. + Conv::AvrInterrupt => FnAttribute::AvrSignal, + Conv::AvrNonBlockingInterrupt => FnAttribute::AvrInterrupt, + Conv::ArmAapcs => FnAttribute::ArmPcs("aapcs"), + Conv::Msp430Intr => FnAttribute::Msp430Interrupt, + Conv::RiscvInterrupt { kind } => { + let kind = match kind { + RiscvInterruptKind::Machine => "machine", + RiscvInterruptKind::Supervisor => "supervisor", + }; + FnAttribute::RiscvInterrupt(kind) + } + Conv::X86Fastcall => FnAttribute::X86FastCall, + Conv::X86Intr => FnAttribute::X86Interrupt, + Conv::X86Stdcall => FnAttribute::X86Stdcall, + Conv::X86ThisCall => FnAttribute::X86ThisCall, + // NOTE: the vectorcall calling convention is not yet implemented in GCC: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 Conv::X86VectorCall => return None, - Conv::X86_64SysV => FnAttribute::SysvAbi, - Conv::X86_64Win64 => FnAttribute::MsAbi, + Conv::X86_64SysV => FnAttribute::X86SysvAbi, + Conv::X86_64Win64 => FnAttribute::X86MsAbi, }; Some(attribute) } diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index 69b04dd57969..e63091c6082a 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -6,21 +6,69 @@ use rustc_attr_parsing::InlineAttr; use rustc_attr_parsing::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +#[cfg(feature = "master")] +use rustc_middle::mir::TerminatorKind; use rustc_middle::ty; use crate::context::CodegenCx; use crate::gcc_util::to_gcc_features; -/// Get GCC attribute for the provided inline heuristic. +/// Checks if the function `instance` is recursively inline. +/// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive. +#[cfg(feature = "master")] +fn resursively_inline<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + instance: ty::Instance<'tcx>, +) -> bool { + // No body, so we can't check if this is recursively inline, so we assume it is. + if !cx.tcx.is_mir_available(instance.def_id()) { + return true; + } + // `expect_local` ought to never fail: we should be checking a function within this codegen unit. + let body = cx.tcx.optimized_mir(instance.def_id()); + for block in body.basic_blocks.iter() { + let Some(ref terminator) = block.terminator else { continue }; + // I assume that the recursive-inline issue applies only to functions, and not to drops. + // In principle, a recursive, `#[inline(always)]` drop could(?) exist, but I don't think it does. + let TerminatorKind::Call { ref func, .. } = terminator.kind else { continue }; + let Some((def, _args)) = func.const_fn_def() else { continue }; + // Check if the called function is recursively inline. + if matches!( + cx.tcx.codegen_fn_attrs(def).inline, + InlineAttr::Always | InlineAttr::Force { .. } + ) { + return true; + } + } + false +} + +/// Get GCC attribute for the provided inline heuristic, attached to `instance`. #[cfg(feature = "master")] #[inline] fn inline_attr<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, inline: InlineAttr, + instance: ty::Instance<'tcx>, ) -> Option> { match inline { + InlineAttr::Always => { + // We can't simply always return `always_inline` unconditionally. + // It is *NOT A HINT* and does not work for recursive functions. + // + // So, it can only be applied *if*: + // The current function does not call any functions marked `#[inline(always)]`. + // + // That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost. + // We *only* need to check all the terminators of a function marked with this attribute. + if resursively_inline(cx, instance) { + Some(FnAttribute::Inline) + } else { + Some(FnAttribute::AlwaysInline) + } + } InlineAttr::Hint => Some(FnAttribute::Inline), - InlineAttr::Always | InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), + InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), InlineAttr::Never => { if cx.sess().target.arch != "amdgpu" { Some(FnAttribute::NoInline) @@ -52,7 +100,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } else { codegen_fn_attrs.inline }; - if let Some(attr) = inline_attr(cx, inline) { + if let Some(attr) = inline_attr(cx, inline, instance) { if let FnAttribute::AlwaysInline = attr { func.add_attribute(FnAttribute::Inline); } @@ -88,14 +136,8 @@ pub fn from_fn_attrs<'gcc, 'tcx>( let target_features = function_features .iter() .filter_map(|feature| { - // FIXME(antoyo): for some reasons, disabling SSE results in the following error when - // compiling Rust for Linux: - // SSE register return with SSE disabled - // TODO(antoyo): support soft-float and retpoline-external-thunk. - if feature.contains("soft-float") - || feature.contains("retpoline-external-thunk") - || *feature == "-sse" - { + // TODO(antoyo): support soft-float. + if feature.contains("soft-float") { return None; } diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index faeb2643ecb8..e9c87f357793 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -593,7 +593,7 @@ fn thin_lto( Ok((opt_jobs, copy_jobs)) } -pub unsafe fn optimize_thin_module( +pub fn optimize_thin_module( thin_module: ThinModule, _cgcx: &CodegenContext, ) -> Result, FatalError> { diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 16c895322e88..09e955acf390 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -14,7 +14,7 @@ use crate::base::add_pic_option; use crate::errors::CopyBitcode; use crate::{GccCodegenBackend, GccContext}; -pub(crate) unsafe fn codegen( +pub(crate) fn codegen( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 9e5ebf3a9a40..4e2163201fd0 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -568,11 +568,28 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ) { let mut gcc_cases = vec![]; let typ = self.val_ty(value); - for (on_val, dest) in cases { - let on_val = self.const_uint_big(typ, on_val); - gcc_cases.push(self.context.new_case(on_val, on_val, dest)); + // FIXME(FractalFir): This is a workaround for a libgccjit limitation. + // Currently, libgccjit can't directly create 128 bit integers. + // Since switch cases must be values, and casts are not constant, we can't use 128 bit switch cases. + // In such a case, we will simply fall back to an if-ladder. + // This *may* be slower than a native switch, but a slow working solution is better than none at all. + if typ.is_i128(self) || typ.is_u128(self) { + for (on_val, dest) in cases { + let on_val = self.const_uint_big(typ, on_val); + let is_case = + self.context.new_comparison(self.location, ComparisonOp::Equals, value, on_val); + let next_block = self.current_func().new_block("case"); + self.block.end_with_conditional(self.location, is_case, dest, next_block); + self.block = next_block; + } + self.block.end_with_jump(self.location, default_block); + } else { + for (on_val, dest) in cases { + let on_val = self.const_uint_big(typ, on_val); + gcc_cases.push(self.context.new_case(on_val, on_val, dest)); + } + self.block.end_with_switch(self.location, value, default_block, &gcc_cases); } - self.block.end_with_switch(self.location, value, default_block, &gcc_cases); } #[cfg(feature = "master")] diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 0a67bd7bc71a..033afc0f8fbf 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -191,13 +191,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { // TODO(antoyo): check if it's okay that no link_section is set. let typ = self.val_ty(cv).get_aligned(align.bytes()); - let global = self.declare_private_global(&name[..], typ); - global + self.declare_private_global(&name[..], typ) } _ => { let typ = self.val_ty(cv).get_aligned(align.bytes()); - let global = self.declare_unnamed_global(typ); - global + self.declare_unnamed_global(typ) } }; global.global_set_initializer_rvalue(cv); diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index f3ced8643952..e0597d0030d5 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -289,7 +289,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { ) -> Self::DILocation { let pos = span.lo(); let DebugLoc { file, line, col } = self.lookup_debug_loc(pos); - let loc = match file.name { + match file.name { rustc_span::FileName::Real(ref name) => match *name { rustc_span::RealFileName::LocalPath(ref name) => { if let Some(name) = name.to_str() { @@ -314,7 +314,6 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } }, _ => Location::null(), - }; - loc + } } } diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index c1ca3eb849e8..bed82073e2c4 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -157,6 +157,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. +#[allow(clippy::let_and_return)] fn declare_raw_fn<'gcc>( cx: &CodegenCx<'gcc, '_>, name: &str, diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs index b8d1cde1d5dd..5ada535aa41d 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs @@ -38,6 +38,7 @@ match name { "llvm.aarch64.gcsss" => "__builtin_arm_gcsss", "llvm.aarch64.isb" => "__builtin_arm_isb", "llvm.aarch64.prefetch" => "__builtin_arm_prefetch", + "llvm.aarch64.sme.in.streaming.mode" => "__builtin_arm_in_streaming_mode", "llvm.aarch64.sve.aesd" => "__builtin_sve_svaesd_u8", "llvm.aarch64.sve.aese" => "__builtin_sve_svaese_u8", "llvm.aarch64.sve.aesimc" => "__builtin_sve_svaesimc_u8", @@ -55,6 +56,8 @@ match name { "llvm.aarch64.ttest" => "__builtin_arm_ttest", // amdgcn "llvm.amdgcn.alignbyte" => "__builtin_amdgcn_alignbyte", + "llvm.amdgcn.ashr.pk.i8.i32" => "__builtin_amdgcn_ashr_pk_i8_i32", + "llvm.amdgcn.ashr.pk.u8.i32" => "__builtin_amdgcn_ashr_pk_u8_i32", "llvm.amdgcn.buffer.wbinvl1" => "__builtin_amdgcn_buffer_wbinvl1", "llvm.amdgcn.buffer.wbinvl1.sc" => "__builtin_amdgcn_buffer_wbinvl1_sc", "llvm.amdgcn.buffer.wbinvl1.vol" => "__builtin_amdgcn_buffer_wbinvl1_vol", @@ -64,6 +67,7 @@ match name { "llvm.amdgcn.cubetc" => "__builtin_amdgcn_cubetc", "llvm.amdgcn.cvt.f32.bf8" => "__builtin_amdgcn_cvt_f32_bf8", "llvm.amdgcn.cvt.f32.fp8" => "__builtin_amdgcn_cvt_f32_fp8", + "llvm.amdgcn.cvt.off.f32.i4" => "__builtin_amdgcn_cvt_off_f32_i4", "llvm.amdgcn.cvt.pk.bf8.f32" => "__builtin_amdgcn_cvt_pk_bf8_f32", "llvm.amdgcn.cvt.pk.f32.bf8" => "__builtin_amdgcn_cvt_pk_f32_bf8", "llvm.amdgcn.cvt.pk.f32.fp8" => "__builtin_amdgcn_cvt_pk_f32_fp8", @@ -74,7 +78,58 @@ match name { "llvm.amdgcn.cvt.pknorm.i16" => "__builtin_amdgcn_cvt_pknorm_i16", "llvm.amdgcn.cvt.pknorm.u16" => "__builtin_amdgcn_cvt_pknorm_u16", "llvm.amdgcn.cvt.pkrtz" => "__builtin_amdgcn_cvt_pkrtz", + "llvm.amdgcn.cvt.scalef32.2xpk16.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_bf6_f32", + "llvm.amdgcn.cvt.scalef32.2xpk16.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_fp6_f32", + "llvm.amdgcn.cvt.scalef32.f16.bf8" => "__builtin_amdgcn_cvt_scalef32_f16_bf8", + "llvm.amdgcn.cvt.scalef32.f16.fp8" => "__builtin_amdgcn_cvt_scalef32_f16_fp8", + "llvm.amdgcn.cvt.scalef32.f32.bf8" => "__builtin_amdgcn_cvt_scalef32_f32_bf8", + "llvm.amdgcn.cvt.scalef32.f32.fp8" => "__builtin_amdgcn_cvt_scalef32_f32_fp8", + "llvm.amdgcn.cvt.scalef32.pk.bf16.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_bf8", + "llvm.amdgcn.cvt.scalef32.pk.bf16.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_fp4", + "llvm.amdgcn.cvt.scalef32.pk.bf16.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_fp8", + "llvm.amdgcn.cvt.scalef32.pk.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_bf16", + "llvm.amdgcn.cvt.scalef32.pk.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_f16", + "llvm.amdgcn.cvt.scalef32.pk.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_f32", + "llvm.amdgcn.cvt.scalef32.pk.f16.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_f16_bf8", + "llvm.amdgcn.cvt.scalef32.pk.f16.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_f16_fp4", + "llvm.amdgcn.cvt.scalef32.pk.f16.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_f16_fp8", + "llvm.amdgcn.cvt.scalef32.pk.f32.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_f32_bf8", + "llvm.amdgcn.cvt.scalef32.pk.f32.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_f32_fp4", + "llvm.amdgcn.cvt.scalef32.pk.f32.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_f32_fp8", + "llvm.amdgcn.cvt.scalef32.pk.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_bf16", + "llvm.amdgcn.cvt.scalef32.pk.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_f16", + "llvm.amdgcn.cvt.scalef32.pk.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_f32", + "llvm.amdgcn.cvt.scalef32.pk.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_bf16", + "llvm.amdgcn.cvt.scalef32.pk.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f16", + "llvm.amdgcn.cvt.scalef32.pk.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f32", + "llvm.amdgcn.cvt.scalef32.pk32.bf16.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.bf16.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_bf6_bf16", + "llvm.amdgcn.cvt.scalef32.pk32.bf6.f16" => "__builtin_amdgcn_cvt_scalef32_pk32_bf6_f16", + "llvm.amdgcn.cvt.scalef32.pk32.f16.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_f16_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.f16.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_f16_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.f32.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_f32_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.f32.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_f32_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_bf16", + "llvm.amdgcn.cvt.scalef32.pk32.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_f16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_bf16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f32", + "llvm.amdgcn.cvt.scalef32.sr.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_bf16", + "llvm.amdgcn.cvt.scalef32.sr.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_f16", + "llvm.amdgcn.cvt.scalef32.sr.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_f32", + "llvm.amdgcn.cvt.sr.bf16.f32" => "__builtin_amdgcn_cvt_sr_bf16_f32", "llvm.amdgcn.cvt.sr.bf8.f32" => "__builtin_amdgcn_cvt_sr_bf8_f32", + "llvm.amdgcn.cvt.sr.f16.f32" => "__builtin_amdgcn_cvt_sr_f16_f32", "llvm.amdgcn.cvt.sr.fp8.f32" => "__builtin_amdgcn_cvt_sr_fp8_f32", "llvm.amdgcn.dispatch.id" => "__builtin_amdgcn_dispatch_id", "llvm.amdgcn.dot4.f32.bf8.bf8" => "__builtin_amdgcn_dot4_f32_bf8_bf8", @@ -83,6 +138,7 @@ match name { "llvm.amdgcn.dot4.f32.fp8.fp8" => "__builtin_amdgcn_dot4_f32_fp8_fp8", "llvm.amdgcn.ds.add.gs.reg.rtn" => "__builtin_amdgcn_ds_add_gs_reg_rtn", "llvm.amdgcn.ds.bpermute" => "__builtin_amdgcn_ds_bpermute", + "llvm.amdgcn.ds.bpermute.fi.b32" => "__builtin_amdgcn_ds_bpermute_fi_b32", "llvm.amdgcn.ds.gws.barrier" => "__builtin_amdgcn_ds_gws_barrier", "llvm.amdgcn.ds.gws.init" => "__builtin_amdgcn_ds_gws_init", "llvm.amdgcn.ds.gws.sema.br" => "__builtin_amdgcn_ds_gws_sema_br", @@ -97,6 +153,7 @@ match name { "llvm.amdgcn.fdot2.bf16.bf16" => "__builtin_amdgcn_fdot2_bf16_bf16", "llvm.amdgcn.fdot2.f16.f16" => "__builtin_amdgcn_fdot2_f16_f16", "llvm.amdgcn.fdot2.f32.bf16" => "__builtin_amdgcn_fdot2_f32_bf16", + "llvm.amdgcn.fdot2c.f32.bf16" => "__builtin_amdgcn_fdot2c_f32_bf16", "llvm.amdgcn.fmul.legacy" => "__builtin_amdgcn_fmul_legacy", "llvm.amdgcn.global.load.lds" => "__builtin_amdgcn_global_load_lds", "llvm.amdgcn.groupstaticsize" => "__builtin_amdgcn_groupstaticsize", @@ -118,8 +175,10 @@ match name { "llvm.amdgcn.mfma.f32.16x16x16f16" => "__builtin_amdgcn_mfma_f32_16x16x16f16", "llvm.amdgcn.mfma.f32.16x16x1f32" => "__builtin_amdgcn_mfma_f32_16x16x1f32", "llvm.amdgcn.mfma.f32.16x16x2bf16" => "__builtin_amdgcn_mfma_f32_16x16x2bf16", + "llvm.amdgcn.mfma.f32.16x16x32.bf16" => "__builtin_amdgcn_mfma_f32_16x16x32_bf16", "llvm.amdgcn.mfma.f32.16x16x32.bf8.bf8" => "__builtin_amdgcn_mfma_f32_16x16x32_bf8_bf8", "llvm.amdgcn.mfma.f32.16x16x32.bf8.fp8" => "__builtin_amdgcn_mfma_f32_16x16x32_bf8_fp8", + "llvm.amdgcn.mfma.f32.16x16x32.f16" => "__builtin_amdgcn_mfma_f32_16x16x32_f16", "llvm.amdgcn.mfma.f32.16x16x32.fp8.bf8" => "__builtin_amdgcn_mfma_f32_16x16x32_fp8_bf8", "llvm.amdgcn.mfma.f32.16x16x32.fp8.fp8" => "__builtin_amdgcn_mfma_f32_16x16x32_fp8_fp8", "llvm.amdgcn.mfma.f32.16x16x4bf16.1k" => "__builtin_amdgcn_mfma_f32_16x16x4bf16_1k", @@ -127,8 +186,10 @@ match name { "llvm.amdgcn.mfma.f32.16x16x4f32" => "__builtin_amdgcn_mfma_f32_16x16x4f32", "llvm.amdgcn.mfma.f32.16x16x8.xf32" => "__builtin_amdgcn_mfma_f32_16x16x8_xf32", "llvm.amdgcn.mfma.f32.16x16x8bf16" => "__builtin_amdgcn_mfma_f32_16x16x8bf16", + "llvm.amdgcn.mfma.f32.32x32x16.bf16" => "__builtin_amdgcn_mfma_f32_32x32x16_bf16", "llvm.amdgcn.mfma.f32.32x32x16.bf8.bf8" => "__builtin_amdgcn_mfma_f32_32x32x16_bf8_bf8", "llvm.amdgcn.mfma.f32.32x32x16.bf8.fp8" => "__builtin_amdgcn_mfma_f32_32x32x16_bf8_fp8", + "llvm.amdgcn.mfma.f32.32x32x16.f16" => "__builtin_amdgcn_mfma_f32_32x32x16_f16", "llvm.amdgcn.mfma.f32.32x32x16.fp8.bf8" => "__builtin_amdgcn_mfma_f32_32x32x16_fp8_bf8", "llvm.amdgcn.mfma.f32.32x32x16.fp8.fp8" => "__builtin_amdgcn_mfma_f32_32x32x16_fp8_fp8", "llvm.amdgcn.mfma.f32.32x32x1f32" => "__builtin_amdgcn_mfma_f32_32x32x1f32", @@ -149,7 +210,9 @@ match name { "llvm.amdgcn.mfma.i32.16x16x16i8" => "__builtin_amdgcn_mfma_i32_16x16x16i8", "llvm.amdgcn.mfma.i32.16x16x32.i8" => "__builtin_amdgcn_mfma_i32_16x16x32_i8", "llvm.amdgcn.mfma.i32.16x16x4i8" => "__builtin_amdgcn_mfma_i32_16x16x4i8", + "llvm.amdgcn.mfma.i32.16x16x64.i8" => "__builtin_amdgcn_mfma_i32_16x16x64_i8", "llvm.amdgcn.mfma.i32.32x32x16.i8" => "__builtin_amdgcn_mfma_i32_32x32x16_i8", + "llvm.amdgcn.mfma.i32.32x32x32.i8" => "__builtin_amdgcn_mfma_i32_32x32x32_i8", "llvm.amdgcn.mfma.i32.32x32x4i8" => "__builtin_amdgcn_mfma_i32_32x32x4i8", "llvm.amdgcn.mfma.i32.32x32x8i8" => "__builtin_amdgcn_mfma_i32_32x32x8i8", "llvm.amdgcn.mfma.i32.4x4x4i8" => "__builtin_amdgcn_mfma_i32_4x4x4i8", @@ -159,25 +222,25 @@ match name { "llvm.amdgcn.perm" => "__builtin_amdgcn_perm", "llvm.amdgcn.permlane16.var" => "__builtin_amdgcn_permlane16_var", "llvm.amdgcn.permlanex16.var" => "__builtin_amdgcn_permlanex16_var", + "llvm.amdgcn.prng.b32" => "__builtin_amdgcn_prng_b32", "llvm.amdgcn.qsad.pk.u16.u8" => "__builtin_amdgcn_qsad_pk_u16_u8", "llvm.amdgcn.queue.ptr" => "__builtin_amdgcn_queue_ptr", + "llvm.amdgcn.raw.ptr.buffer.load.lds" => "__builtin_amdgcn_raw_ptr_buffer_load_lds", "llvm.amdgcn.rcp.legacy" => "__builtin_amdgcn_rcp_legacy", "llvm.amdgcn.rsq.legacy" => "__builtin_amdgcn_rsq_legacy", "llvm.amdgcn.s.barrier" => "__builtin_amdgcn_s_barrier", - "llvm.amdgcn.s.barrier.init" => "__builtin_amdgcn_s_barrier_init", - "llvm.amdgcn.s.barrier.join" => "__builtin_amdgcn_s_barrier_join", - "llvm.amdgcn.s.barrier.leave" => "__builtin_amdgcn_s_barrier_leave", "llvm.amdgcn.s.barrier.signal" => "__builtin_amdgcn_s_barrier_signal", "llvm.amdgcn.s.barrier.signal.isfirst" => "__builtin_amdgcn_s_barrier_signal_isfirst", - "llvm.amdgcn.s.barrier.signal.isfirst.var" => "__builtin_amdgcn_s_barrier_signal_isfirst_var", "llvm.amdgcn.s.barrier.signal.var" => "__builtin_amdgcn_s_barrier_signal_var", "llvm.amdgcn.s.barrier.wait" => "__builtin_amdgcn_s_barrier_wait", + "llvm.amdgcn.s.buffer.prefetch.data" => "__builtin_amdgcn_s_buffer_prefetch_data", "llvm.amdgcn.s.dcache.inv" => "__builtin_amdgcn_s_dcache_inv", "llvm.amdgcn.s.dcache.inv.vol" => "__builtin_amdgcn_s_dcache_inv_vol", "llvm.amdgcn.s.dcache.wb" => "__builtin_amdgcn_s_dcache_wb", "llvm.amdgcn.s.dcache.wb.vol" => "__builtin_amdgcn_s_dcache_wb_vol", "llvm.amdgcn.s.decperflevel" => "__builtin_amdgcn_s_decperflevel", "llvm.amdgcn.s.get.barrier.state" => "__builtin_amdgcn_s_get_barrier_state", + "llvm.amdgcn.s.get.named.barrier.state" => "__builtin_amdgcn_s_get_named_barrier_state", "llvm.amdgcn.s.get.waveid.in.workgroup" => "__builtin_amdgcn_s_get_waveid_in_workgroup", "llvm.amdgcn.s.getpc" => "__builtin_amdgcn_s_getpc", "llvm.amdgcn.s.getreg" => "__builtin_amdgcn_s_getreg", @@ -194,7 +257,6 @@ match name { "llvm.amdgcn.s.ttracedata.imm" => "__builtin_amdgcn_s_ttracedata_imm", "llvm.amdgcn.s.wait.event.export.ready" => "__builtin_amdgcn_s_wait_event_export_ready", "llvm.amdgcn.s.waitcnt" => "__builtin_amdgcn_s_waitcnt", - "llvm.amdgcn.s.wakeup.barrier" => "__builtin_amdgcn_s_wakeup_barrier", "llvm.amdgcn.sad.hi.u8" => "__builtin_amdgcn_sad_hi_u8", "llvm.amdgcn.sad.u16" => "__builtin_amdgcn_sad_u16", "llvm.amdgcn.sad.u8" => "__builtin_amdgcn_sad_u8", @@ -203,20 +265,34 @@ match name { "llvm.amdgcn.sdot2" => "__builtin_amdgcn_sdot2", "llvm.amdgcn.sdot4" => "__builtin_amdgcn_sdot4", "llvm.amdgcn.sdot8" => "__builtin_amdgcn_sdot8", + "llvm.amdgcn.smfmac.f32.16x16x128.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x128_bf8_bf8", + "llvm.amdgcn.smfmac.f32.16x16x128.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x128_bf8_fp8", + "llvm.amdgcn.smfmac.f32.16x16x128.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x128_fp8_bf8", + "llvm.amdgcn.smfmac.f32.16x16x128.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x128_fp8_fp8", "llvm.amdgcn.smfmac.f32.16x16x32.bf16" => "__builtin_amdgcn_smfmac_f32_16x16x32_bf16", "llvm.amdgcn.smfmac.f32.16x16x32.f16" => "__builtin_amdgcn_smfmac_f32_16x16x32_f16", + "llvm.amdgcn.smfmac.f32.16x16x64.bf16" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf16", "llvm.amdgcn.smfmac.f32.16x16x64.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf8_bf8", "llvm.amdgcn.smfmac.f32.16x16x64.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf8_fp8", + "llvm.amdgcn.smfmac.f32.16x16x64.f16" => "__builtin_amdgcn_smfmac_f32_16x16x64_f16", "llvm.amdgcn.smfmac.f32.16x16x64.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x64_fp8_bf8", "llvm.amdgcn.smfmac.f32.16x16x64.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x64_fp8_fp8", "llvm.amdgcn.smfmac.f32.32x32x16.bf16" => "__builtin_amdgcn_smfmac_f32_32x32x16_bf16", "llvm.amdgcn.smfmac.f32.32x32x16.f16" => "__builtin_amdgcn_smfmac_f32_32x32x16_f16", + "llvm.amdgcn.smfmac.f32.32x32x32.bf16" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf16", "llvm.amdgcn.smfmac.f32.32x32x32.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf8_bf8", "llvm.amdgcn.smfmac.f32.32x32x32.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x32.f16" => "__builtin_amdgcn_smfmac_f32_32x32x32_f16", "llvm.amdgcn.smfmac.f32.32x32x32.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x32_fp8_bf8", "llvm.amdgcn.smfmac.f32.32x32x32.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x32_fp8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x64.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x64_bf8_bf8", + "llvm.amdgcn.smfmac.f32.32x32x64.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x64_bf8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x64.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x64_fp8_bf8", + "llvm.amdgcn.smfmac.f32.32x32x64.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x64_fp8_fp8", + "llvm.amdgcn.smfmac.i32.16x16x128.i8" => "__builtin_amdgcn_smfmac_i32_16x16x128_i8", "llvm.amdgcn.smfmac.i32.16x16x64.i8" => "__builtin_amdgcn_smfmac_i32_16x16x64_i8", "llvm.amdgcn.smfmac.i32.32x32x32.i8" => "__builtin_amdgcn_smfmac_i32_32x32x32_i8", + "llvm.amdgcn.smfmac.i32.32x32x64.i8" => "__builtin_amdgcn_smfmac_i32_32x32x64_i8", "llvm.amdgcn.sudot4" => "__builtin_amdgcn_sudot4", "llvm.amdgcn.sudot8" => "__builtin_amdgcn_sudot8", "llvm.amdgcn.udot2" => "__builtin_amdgcn_udot2", @@ -227,6 +303,9 @@ match name { "llvm.amdgcn.workgroup.id.x" => "__builtin_amdgcn_workgroup_id_x", "llvm.amdgcn.workgroup.id.y" => "__builtin_amdgcn_workgroup_id_y", "llvm.amdgcn.workgroup.id.z" => "__builtin_amdgcn_workgroup_id_z", + "llvm.amdgcn.workitem.id.x" => "__builtin_amdgcn_workitem_id_x", + "llvm.amdgcn.workitem.id.y" => "__builtin_amdgcn_workitem_id_y", + "llvm.amdgcn.workitem.id.z" => "__builtin_amdgcn_workitem_id_z", // arm "llvm.arm.cdp" => "__builtin_arm_cdp", "llvm.arm.cdp2" => "__builtin_arm_cdp2", @@ -342,8 +421,6 @@ match name { "llvm.bpf.pseudo" => "__builtin_bpf_pseudo", // cuda "llvm.cuda.syncthreads" => "__syncthreads", - // dx - "llvm.dx.create.handle" => "__builtin_hlsl_create_handle", // hexagon "llvm.hexagon.A2.abs" => "__builtin_HEXAGON_A2_abs", "llvm.hexagon.A2.absp" => "__builtin_HEXAGON_A2_absp", @@ -1255,6 +1332,10 @@ match name { "llvm.hexagon.SI.to.SXTHI.asrh" => "__builtin_SI_to_SXTHI_asrh", "llvm.hexagon.V6.extractw" => "__builtin_HEXAGON_V6_extractw", "llvm.hexagon.V6.extractw.128B" => "__builtin_HEXAGON_V6_extractw_128B", + "llvm.hexagon.V6.get.qfext" => "__builtin_HEXAGON_V6_get_qfext", + "llvm.hexagon.V6.get.qfext.128B" => "__builtin_HEXAGON_V6_get_qfext_128B", + "llvm.hexagon.V6.get.qfext.oracc" => "__builtin_HEXAGON_V6_get_qfext_oracc", + "llvm.hexagon.V6.get.qfext.oracc.128B" => "__builtin_HEXAGON_V6_get_qfext_oracc_128B", "llvm.hexagon.V6.hi" => "__builtin_HEXAGON_V6_hi", "llvm.hexagon.V6.hi.128B" => "__builtin_HEXAGON_V6_hi_128B", "llvm.hexagon.V6.lo" => "__builtin_HEXAGON_V6_lo", @@ -1281,6 +1362,8 @@ match name { "llvm.hexagon.V6.pred.scalar2v2.128B" => "__builtin_HEXAGON_V6_pred_scalar2v2_128B", "llvm.hexagon.V6.pred.xor" => "__builtin_HEXAGON_V6_pred_xor", "llvm.hexagon.V6.pred.xor.128B" => "__builtin_HEXAGON_V6_pred_xor_128B", + "llvm.hexagon.V6.set.qfext" => "__builtin_HEXAGON_V6_set_qfext", + "llvm.hexagon.V6.set.qfext.128B" => "__builtin_HEXAGON_V6_set_qfext_128B", "llvm.hexagon.V6.shuffeqh" => "__builtin_HEXAGON_V6_shuffeqh", "llvm.hexagon.V6.shuffeqh.128B" => "__builtin_HEXAGON_V6_shuffeqh_128B", "llvm.hexagon.V6.shuffeqw" => "__builtin_HEXAGON_V6_shuffeqw", @@ -1301,6 +1384,8 @@ match name { "llvm.hexagon.V6.vS32b.nt.qpred.ai.128B" => "__builtin_HEXAGON_V6_vS32b_nt_qpred_ai_128B", "llvm.hexagon.V6.vS32b.qpred.ai" => "__builtin_HEXAGON_V6_vS32b_qpred_ai", "llvm.hexagon.V6.vS32b.qpred.ai.128B" => "__builtin_HEXAGON_V6_vS32b_qpred_ai_128B", + "llvm.hexagon.V6.vabs.f8" => "__builtin_HEXAGON_V6_vabs_f8", + "llvm.hexagon.V6.vabs.f8.128B" => "__builtin_HEXAGON_V6_vabs_f8_128B", "llvm.hexagon.V6.vabs.hf" => "__builtin_HEXAGON_V6_vabs_hf", "llvm.hexagon.V6.vabs.hf.128B" => "__builtin_HEXAGON_V6_vabs_hf_128B", "llvm.hexagon.V6.vabs.sf" => "__builtin_HEXAGON_V6_vabs_sf", @@ -1327,6 +1412,8 @@ match name { "llvm.hexagon.V6.vabsw.sat.128B" => "__builtin_HEXAGON_V6_vabsw_sat_128B", "llvm.hexagon.V6.vadd.hf" => "__builtin_HEXAGON_V6_vadd_hf", "llvm.hexagon.V6.vadd.hf.128B" => "__builtin_HEXAGON_V6_vadd_hf_128B", + "llvm.hexagon.V6.vadd.hf.f8" => "__builtin_HEXAGON_V6_vadd_hf_f8", + "llvm.hexagon.V6.vadd.hf.f8.128B" => "__builtin_HEXAGON_V6_vadd_hf_f8_128B", "llvm.hexagon.V6.vadd.hf.hf" => "__builtin_HEXAGON_V6_vadd_hf_hf", "llvm.hexagon.V6.vadd.hf.hf.128B" => "__builtin_HEXAGON_V6_vadd_hf_hf_128B", "llvm.hexagon.V6.vadd.qf16" => "__builtin_HEXAGON_V6_vadd_qf16", @@ -1549,10 +1636,14 @@ match name { "llvm.hexagon.V6.vcvt.b.hf.128B" => "__builtin_HEXAGON_V6_vcvt_b_hf_128B", "llvm.hexagon.V6.vcvt.bf.sf" => "__builtin_HEXAGON_V6_vcvt_bf_sf", "llvm.hexagon.V6.vcvt.bf.sf.128B" => "__builtin_HEXAGON_V6_vcvt_bf_sf_128B", + "llvm.hexagon.V6.vcvt.f8.hf" => "__builtin_HEXAGON_V6_vcvt_f8_hf", + "llvm.hexagon.V6.vcvt.f8.hf.128B" => "__builtin_HEXAGON_V6_vcvt_f8_hf_128B", "llvm.hexagon.V6.vcvt.h.hf" => "__builtin_HEXAGON_V6_vcvt_h_hf", "llvm.hexagon.V6.vcvt.h.hf.128B" => "__builtin_HEXAGON_V6_vcvt_h_hf_128B", "llvm.hexagon.V6.vcvt.hf.b" => "__builtin_HEXAGON_V6_vcvt_hf_b", "llvm.hexagon.V6.vcvt.hf.b.128B" => "__builtin_HEXAGON_V6_vcvt_hf_b_128B", + "llvm.hexagon.V6.vcvt.hf.f8" => "__builtin_HEXAGON_V6_vcvt_hf_f8", + "llvm.hexagon.V6.vcvt.hf.f8.128B" => "__builtin_HEXAGON_V6_vcvt_hf_f8_128B", "llvm.hexagon.V6.vcvt.hf.h" => "__builtin_HEXAGON_V6_vcvt_hf_h", "llvm.hexagon.V6.vcvt.hf.h.128B" => "__builtin_HEXAGON_V6_vcvt_hf_h_128B", "llvm.hexagon.V6.vcvt.hf.sf" => "__builtin_HEXAGON_V6_vcvt_hf_sf", @@ -1567,6 +1658,14 @@ match name { "llvm.hexagon.V6.vcvt.ub.hf.128B" => "__builtin_HEXAGON_V6_vcvt_ub_hf_128B", "llvm.hexagon.V6.vcvt.uh.hf" => "__builtin_HEXAGON_V6_vcvt_uh_hf", "llvm.hexagon.V6.vcvt.uh.hf.128B" => "__builtin_HEXAGON_V6_vcvt_uh_hf_128B", + "llvm.hexagon.V6.vcvt2.b.hf" => "__builtin_HEXAGON_V6_vcvt2_b_hf", + "llvm.hexagon.V6.vcvt2.b.hf.128B" => "__builtin_HEXAGON_V6_vcvt2_b_hf_128B", + "llvm.hexagon.V6.vcvt2.hf.b" => "__builtin_HEXAGON_V6_vcvt2_hf_b", + "llvm.hexagon.V6.vcvt2.hf.b.128B" => "__builtin_HEXAGON_V6_vcvt2_hf_b_128B", + "llvm.hexagon.V6.vcvt2.hf.ub" => "__builtin_HEXAGON_V6_vcvt2_hf_ub", + "llvm.hexagon.V6.vcvt2.hf.ub.128B" => "__builtin_HEXAGON_V6_vcvt2_hf_ub_128B", + "llvm.hexagon.V6.vcvt2.ub.hf" => "__builtin_HEXAGON_V6_vcvt2_ub_hf", + "llvm.hexagon.V6.vcvt2.ub.hf.128B" => "__builtin_HEXAGON_V6_vcvt2_ub_hf_128B", "llvm.hexagon.V6.vd0" => "__builtin_HEXAGON_V6_vd0", "llvm.hexagon.V6.vd0.128B" => "__builtin_HEXAGON_V6_vd0_128B", "llvm.hexagon.V6.vdd0" => "__builtin_HEXAGON_V6_vdd0", @@ -1649,14 +1748,20 @@ match name { "llvm.hexagon.V6.veqw.or.128B" => "__builtin_HEXAGON_V6_veqw_or_128B", "llvm.hexagon.V6.veqw.xor" => "__builtin_HEXAGON_V6_veqw_xor", "llvm.hexagon.V6.veqw.xor.128B" => "__builtin_HEXAGON_V6_veqw_xor_128B", + "llvm.hexagon.V6.vfmax.f8" => "__builtin_HEXAGON_V6_vfmax_f8", + "llvm.hexagon.V6.vfmax.f8.128B" => "__builtin_HEXAGON_V6_vfmax_f8_128B", "llvm.hexagon.V6.vfmax.hf" => "__builtin_HEXAGON_V6_vfmax_hf", "llvm.hexagon.V6.vfmax.hf.128B" => "__builtin_HEXAGON_V6_vfmax_hf_128B", "llvm.hexagon.V6.vfmax.sf" => "__builtin_HEXAGON_V6_vfmax_sf", "llvm.hexagon.V6.vfmax.sf.128B" => "__builtin_HEXAGON_V6_vfmax_sf_128B", + "llvm.hexagon.V6.vfmin.f8" => "__builtin_HEXAGON_V6_vfmin_f8", + "llvm.hexagon.V6.vfmin.f8.128B" => "__builtin_HEXAGON_V6_vfmin_f8_128B", "llvm.hexagon.V6.vfmin.hf" => "__builtin_HEXAGON_V6_vfmin_hf", "llvm.hexagon.V6.vfmin.hf.128B" => "__builtin_HEXAGON_V6_vfmin_hf_128B", "llvm.hexagon.V6.vfmin.sf" => "__builtin_HEXAGON_V6_vfmin_sf", "llvm.hexagon.V6.vfmin.sf.128B" => "__builtin_HEXAGON_V6_vfmin_sf_128B", + "llvm.hexagon.V6.vfneg.f8" => "__builtin_HEXAGON_V6_vfneg_f8", + "llvm.hexagon.V6.vfneg.f8.128B" => "__builtin_HEXAGON_V6_vfneg_f8_128B", "llvm.hexagon.V6.vfneg.hf" => "__builtin_HEXAGON_V6_vfneg_hf", "llvm.hexagon.V6.vfneg.hf.128B" => "__builtin_HEXAGON_V6_vfneg_hf_128B", "llvm.hexagon.V6.vfneg.sf" => "__builtin_HEXAGON_V6_vfneg_sf", @@ -1807,6 +1912,8 @@ match name { "llvm.hexagon.V6.vmaxuh.128B" => "__builtin_HEXAGON_V6_vmaxuh_128B", "llvm.hexagon.V6.vmaxw" => "__builtin_HEXAGON_V6_vmaxw", "llvm.hexagon.V6.vmaxw.128B" => "__builtin_HEXAGON_V6_vmaxw_128B", + "llvm.hexagon.V6.vmerge.qf" => "__builtin_HEXAGON_V6_vmerge_qf", + "llvm.hexagon.V6.vmerge.qf.128B" => "__builtin_HEXAGON_V6_vmerge_qf_128B", "llvm.hexagon.V6.vmin.bf" => "__builtin_HEXAGON_V6_vmin_bf", "llvm.hexagon.V6.vmin.bf.128B" => "__builtin_HEXAGON_V6_vmin_bf_128B", "llvm.hexagon.V6.vmin.hf" => "__builtin_HEXAGON_V6_vmin_hf", @@ -1849,6 +1956,10 @@ match name { "llvm.hexagon.V6.vmpauhuhsat.128B" => "__builtin_HEXAGON_V6_vmpauhuhsat_128B", "llvm.hexagon.V6.vmpsuhuhsat" => "__builtin_HEXAGON_V6_vmpsuhuhsat", "llvm.hexagon.V6.vmpsuhuhsat.128B" => "__builtin_HEXAGON_V6_vmpsuhuhsat_128B", + "llvm.hexagon.V6.vmpy.hf.f8" => "__builtin_HEXAGON_V6_vmpy_hf_f8", + "llvm.hexagon.V6.vmpy.hf.f8.128B" => "__builtin_HEXAGON_V6_vmpy_hf_f8_128B", + "llvm.hexagon.V6.vmpy.hf.f8.acc" => "__builtin_HEXAGON_V6_vmpy_hf_f8_acc", + "llvm.hexagon.V6.vmpy.hf.f8.acc.128B" => "__builtin_HEXAGON_V6_vmpy_hf_f8_acc_128B", "llvm.hexagon.V6.vmpy.hf.hf" => "__builtin_HEXAGON_V6_vmpy_hf_hf", "llvm.hexagon.V6.vmpy.hf.hf.128B" => "__builtin_HEXAGON_V6_vmpy_hf_hf_128B", "llvm.hexagon.V6.vmpy.hf.hf.acc" => "__builtin_HEXAGON_V6_vmpy_hf_hf_acc", @@ -1869,6 +1980,12 @@ match name { "llvm.hexagon.V6.vmpy.qf32.qf16.128B" => "__builtin_HEXAGON_V6_vmpy_qf32_qf16_128B", "llvm.hexagon.V6.vmpy.qf32.sf" => "__builtin_HEXAGON_V6_vmpy_qf32_sf", "llvm.hexagon.V6.vmpy.qf32.sf.128B" => "__builtin_HEXAGON_V6_vmpy_qf32_sf_128B", + "llvm.hexagon.V6.vmpy.rt.hf" => "__builtin_HEXAGON_V6_vmpy_rt_hf", + "llvm.hexagon.V6.vmpy.rt.hf.128B" => "__builtin_HEXAGON_V6_vmpy_rt_hf_128B", + "llvm.hexagon.V6.vmpy.rt.qf16" => "__builtin_HEXAGON_V6_vmpy_rt_qf16", + "llvm.hexagon.V6.vmpy.rt.qf16.128B" => "__builtin_HEXAGON_V6_vmpy_rt_qf16_128B", + "llvm.hexagon.V6.vmpy.rt.sf" => "__builtin_HEXAGON_V6_vmpy_rt_sf", + "llvm.hexagon.V6.vmpy.rt.sf.128B" => "__builtin_HEXAGON_V6_vmpy_rt_sf_128B", "llvm.hexagon.V6.vmpy.sf.bf" => "__builtin_HEXAGON_V6_vmpy_sf_bf", "llvm.hexagon.V6.vmpy.sf.bf.128B" => "__builtin_HEXAGON_V6_vmpy_sf_bf_128B", "llvm.hexagon.V6.vmpy.sf.bf.acc" => "__builtin_HEXAGON_V6_vmpy_sf_bf_acc", @@ -2127,6 +2244,8 @@ match name { "llvm.hexagon.V6.vshufoh.128B" => "__builtin_HEXAGON_V6_vshufoh_128B", "llvm.hexagon.V6.vsub.hf" => "__builtin_HEXAGON_V6_vsub_hf", "llvm.hexagon.V6.vsub.hf.128B" => "__builtin_HEXAGON_V6_vsub_hf_128B", + "llvm.hexagon.V6.vsub.hf.f8" => "__builtin_HEXAGON_V6_vsub_hf_f8", + "llvm.hexagon.V6.vsub.hf.f8.128B" => "__builtin_HEXAGON_V6_vsub_hf_f8_128B", "llvm.hexagon.V6.vsub.hf.hf" => "__builtin_HEXAGON_V6_vsub_hf_hf", "llvm.hexagon.V6.vsub.hf.hf.128B" => "__builtin_HEXAGON_V6_vsub_hf_hf_128B", "llvm.hexagon.V6.vsub.qf16" => "__builtin_HEXAGON_V6_vsub_qf16", @@ -4445,8 +4564,6 @@ match name { "llvm.mips.xor.v" => "__builtin_msa_xor_v", "llvm.mips.xori.b" => "__builtin_msa_xori_b", // nvvm - "llvm.nvvm.abs.bf16" => "__nvvm_abs_bf16", - "llvm.nvvm.abs.bf16x2" => "__nvvm_abs_bf16x2", "llvm.nvvm.abs.i" => "__nvvm_abs_i", "llvm.nvvm.abs.ll" => "__nvvm_abs_ll", "llvm.nvvm.activemask" => "__nvvm_activemask", @@ -4473,6 +4590,10 @@ match name { "llvm.nvvm.barrier0.and" => "__nvvm_bar0_and", "llvm.nvvm.barrier0.or" => "__nvvm_bar0_or", "llvm.nvvm.barrier0.popc" => "__nvvm_bar0_popc", + "llvm.nvvm.bf16x2.to.ue8m0x2.rp" => "__nvvm_bf16x2_to_ue8m0x2_rp", + "llvm.nvvm.bf16x2.to.ue8m0x2.rp.satfinite" => "__nvvm_bf16x2_to_ue8m0x2_rp_satfinite", + "llvm.nvvm.bf16x2.to.ue8m0x2.rz" => "__nvvm_bf16x2_to_ue8m0x2_rz", + "llvm.nvvm.bf16x2.to.ue8m0x2.rz.satfinite" => "__nvvm_bf16x2_to_ue8m0x2_rz_satfinite", "llvm.nvvm.bf2h.rn" => "__nvvm_bf2h_rn", "llvm.nvvm.bf2h.rn.ftz" => "__nvvm_bf2h_rn_ftz", "llvm.nvvm.bitcast.d2ll" => "__nvvm_bitcast_d2ll", @@ -4523,6 +4644,8 @@ match name { "llvm.nvvm.d2ull.rz" => "__nvvm_d2ull_rz", "llvm.nvvm.div.approx.f" => "__nvvm_div_approx_f", "llvm.nvvm.div.approx.ftz.f" => "__nvvm_div_approx_ftz_f", + "llvm.nvvm.div.full" => "__nvvm_div_full", + "llvm.nvvm.div.full.ftz" => "__nvvm_div_full_ftz", "llvm.nvvm.div.rm.d" => "__nvvm_div_rm_d", "llvm.nvvm.div.rm.f" => "__nvvm_div_rm_f", "llvm.nvvm.div.rm.ftz.f" => "__nvvm_div_rm_ftz_f", @@ -4535,6 +4658,10 @@ match name { "llvm.nvvm.div.rz.d" => "__nvvm_div_rz_d", "llvm.nvvm.div.rz.f" => "__nvvm_div_rz_f", "llvm.nvvm.div.rz.ftz.f" => "__nvvm_div_rz_ftz_f", + "llvm.nvvm.e2m3x2.to.f16x2.rn" => "__nvvm_e2m3x2_to_f16x2_rn", + "llvm.nvvm.e2m3x2.to.f16x2.rn.relu" => "__nvvm_e2m3x2_to_f16x2_rn_relu", + "llvm.nvvm.e3m2x2.to.f16x2.rn" => "__nvvm_e3m2x2_to_f16x2_rn", + "llvm.nvvm.e3m2x2.to.f16x2.rn.relu" => "__nvvm_e3m2x2_to_f16x2_rn_relu", "llvm.nvvm.e4m3x2.to.f16x2.rn" => "__nvvm_e4m3x2_to_f16x2_rn", "llvm.nvvm.e4m3x2.to.f16x2.rn.relu" => "__nvvm_e4m3x2_to_f16x2_rn_relu", "llvm.nvvm.e5m2x2.to.f16x2.rn" => "__nvvm_e5m2x2_to_f16x2_rn", @@ -4569,7 +4696,16 @@ match name { "llvm.nvvm.f2ll.rp.ftz" => "__nvvm_f2ll_rp_ftz", "llvm.nvvm.f2ll.rz" => "__nvvm_f2ll_rz", "llvm.nvvm.f2ll.rz.ftz" => "__nvvm_f2ll_rz_ftz", + "llvm.nvvm.f2tf32.rn" => "__nvvm_f2tf32_rn", + "llvm.nvvm.f2tf32.rn.relu" => "__nvvm_f2tf32_rn_relu", + "llvm.nvvm.f2tf32.rn.relu.satfinite" => "__nvvm_f2tf32_rn_relu_satfinite", + "llvm.nvvm.f2tf32.rn.satfinite" => "__nvvm_f2tf32_rn_satfinite", "llvm.nvvm.f2tf32.rna" => "__nvvm_f2tf32_rna", + "llvm.nvvm.f2tf32.rna.satfinite" => "__nvvm_f2tf32_rna_satfinite", + "llvm.nvvm.f2tf32.rz" => "__nvvm_f2tf32_rz", + "llvm.nvvm.f2tf32.rz.relu" => "__nvvm_f2tf32_rz_relu", + "llvm.nvvm.f2tf32.rz.relu.satfinite" => "__nvvm_f2tf32_rz_relu_satfinite", + "llvm.nvvm.f2tf32.rz.satfinite" => "__nvvm_f2tf32_rz_satfinite", "llvm.nvvm.f2ui.rm" => "__nvvm_f2ui_rm", "llvm.nvvm.f2ui.rm.ftz" => "__nvvm_f2ui_rm_ftz", "llvm.nvvm.f2ui.rn" => "__nvvm_f2ui_rn", @@ -4589,10 +4725,18 @@ match name { "llvm.nvvm.fabs.d" => "__nvvm_fabs_d", "llvm.nvvm.fabs.f" => "__nvvm_fabs_f", "llvm.nvvm.fabs.ftz.f" => "__nvvm_fabs_ftz_f", + "llvm.nvvm.ff.to.e2m3x2.rn.relu.satfinite" => "__nvvm_ff_to_e2m3x2_rn_relu_satfinite", + "llvm.nvvm.ff.to.e2m3x2.rn.satfinite" => "__nvvm_ff_to_e2m3x2_rn_satfinite", + "llvm.nvvm.ff.to.e3m2x2.rn.relu.satfinite" => "__nvvm_ff_to_e3m2x2_rn_relu_satfinite", + "llvm.nvvm.ff.to.e3m2x2.rn.satfinite" => "__nvvm_ff_to_e3m2x2_rn_satfinite", "llvm.nvvm.ff.to.e4m3x2.rn" => "__nvvm_ff_to_e4m3x2_rn", "llvm.nvvm.ff.to.e4m3x2.rn.relu" => "__nvvm_ff_to_e4m3x2_rn_relu", "llvm.nvvm.ff.to.e5m2x2.rn" => "__nvvm_ff_to_e5m2x2_rn", "llvm.nvvm.ff.to.e5m2x2.rn.relu" => "__nvvm_ff_to_e5m2x2_rn_relu", + "llvm.nvvm.ff.to.ue8m0x2.rp" => "__nvvm_ff_to_ue8m0x2_rp", + "llvm.nvvm.ff.to.ue8m0x2.rp.satfinite" => "__nvvm_ff_to_ue8m0x2_rp_satfinite", + "llvm.nvvm.ff.to.ue8m0x2.rz" => "__nvvm_ff_to_ue8m0x2_rz", + "llvm.nvvm.ff.to.ue8m0x2.rz.satfinite" => "__nvvm_ff_to_ue8m0x2_rz_satfinite", "llvm.nvvm.ff2bf16x2.rn" => "__nvvm_ff2bf16x2_rn", "llvm.nvvm.ff2bf16x2.rn.relu" => "__nvvm_ff2bf16x2_rn_relu", "llvm.nvvm.ff2bf16x2.rz" => "__nvvm_ff2bf16x2_rz", @@ -4862,6 +5006,14 @@ match name { // [DUPLICATE]: "llvm.nvvm.read.ptx.sreg.warpsize" => "__nvvm_read_ptx_sreg_", "llvm.nvvm.redux.sync.add" => "__nvvm_redux_sync_add", "llvm.nvvm.redux.sync.and" => "__nvvm_redux_sync_and", + "llvm.nvvm.redux.sync.fmax" => "__nvvm_redux_sync_fmax", + "llvm.nvvm.redux.sync.fmax.NaN" => "__nvvm_redux_sync_fmax_NaN", + "llvm.nvvm.redux.sync.fmax.abs" => "__nvvm_redux_sync_fmax_abs", + "llvm.nvvm.redux.sync.fmax.abs.NaN" => "__nvvm_redux_sync_fmax_abs_NaN", + "llvm.nvvm.redux.sync.fmin" => "__nvvm_redux_sync_fmin", + "llvm.nvvm.redux.sync.fmin.NaN" => "__nvvm_redux_sync_fmin_NaN", + "llvm.nvvm.redux.sync.fmin.abs" => "__nvvm_redux_sync_fmin_abs", + "llvm.nvvm.redux.sync.fmin.abs.NaN" => "__nvvm_redux_sync_fmin_abs_NaN", "llvm.nvvm.redux.sync.max" => "__nvvm_redux_sync_max", "llvm.nvvm.redux.sync.min" => "__nvvm_redux_sync_min", "llvm.nvvm.redux.sync.or" => "__nvvm_redux_sync_or", @@ -5149,6 +5301,7 @@ match name { "llvm.nvvm.txq.num.mipmap.levels" => "__nvvm_txq_num_mipmap_levels", "llvm.nvvm.txq.num.samples" => "__nvvm_txq_num_samples", "llvm.nvvm.txq.width" => "__nvvm_txq_width", + "llvm.nvvm.ue8m0x2.to.bf16x2" => "__nvvm_ue8m0x2_to_bf16x2", "llvm.nvvm.ui2d.rm" => "__nvvm_ui2d_rm", "llvm.nvvm.ui2d.rn" => "__nvvm_ui2d_rn", "llvm.nvvm.ui2d.rp" => "__nvvm_ui2d_rp", @@ -5783,6 +5936,9 @@ match name { "llvm.r600.read.tgid.x" => "__builtin_r600_read_tgid_x", "llvm.r600.read.tgid.y" => "__builtin_r600_read_tgid_y", "llvm.r600.read.tgid.z" => "__builtin_r600_read_tgid_z", + "llvm.r600.read.tidig.x" => "__builtin_r600_read_tidig_x", + "llvm.r600.read.tidig.y" => "__builtin_r600_read_tidig_y", + "llvm.r600.read.tidig.z" => "__builtin_r600_read_tidig_z", // riscv "llvm.riscv.aes32dsi" => "__builtin_riscv_aes32dsi", "llvm.riscv.aes32dsmi" => "__builtin_riscv_aes32dsmi", @@ -5806,6 +5962,8 @@ match name { "llvm.riscv.sha512sum1" => "__builtin_riscv_sha512sum1", "llvm.riscv.sha512sum1r" => "__builtin_riscv_sha512sum1r", // s390 + "llvm.s390.bdepg" => "__builtin_s390_bdepg", + "llvm.s390.bextg" => "__builtin_s390_bextg", "llvm.s390.efpc" => "__builtin_s390_efpc", "llvm.s390.etnd" => "__builtin_tx_nesting_depth", "llvm.s390.lcbb" => "__builtin_s390_lcbb", @@ -5828,6 +5986,8 @@ match name { "llvm.s390.vavglf" => "__builtin_s390_vavglf", "llvm.s390.vavglg" => "__builtin_s390_vavglg", "llvm.s390.vavglh" => "__builtin_s390_vavglh", + "llvm.s390.vavglq" => "__builtin_s390_vavglq", + "llvm.s390.vavgq" => "__builtin_s390_vavgq", "llvm.s390.vbperm" => "__builtin_s390_vbperm", "llvm.s390.vcfn" => "__builtin_s390_vcfn", "llvm.s390.vcksm" => "__builtin_s390_vcksm", @@ -5839,6 +5999,7 @@ match name { "llvm.s390.verimf" => "__builtin_s390_verimf", "llvm.s390.verimg" => "__builtin_s390_verimg", "llvm.s390.verimh" => "__builtin_s390_verimh", + "llvm.s390.veval" => "__builtin_s390_veval", "llvm.s390.vfaeb" => "__builtin_s390_vfaeb", "llvm.s390.vfaef" => "__builtin_s390_vfaef", "llvm.s390.vfaeh" => "__builtin_s390_vfaeh", @@ -5857,6 +6018,11 @@ match name { "llvm.s390.vfenezb" => "__builtin_s390_vfenezb", "llvm.s390.vfenezf" => "__builtin_s390_vfenezf", "llvm.s390.vfenezh" => "__builtin_s390_vfenezh", + "llvm.s390.vgemb" => "__builtin_s390_vgemb", + "llvm.s390.vgemf" => "__builtin_s390_vgemf", + "llvm.s390.vgemg" => "__builtin_s390_vgemg", + "llvm.s390.vgemh" => "__builtin_s390_vgemh", + "llvm.s390.vgemq" => "__builtin_s390_vgemq", "llvm.s390.vgfmab" => "__builtin_s390_vgfmab", "llvm.s390.vgfmaf" => "__builtin_s390_vgfmaf", "llvm.s390.vgfmag" => "__builtin_s390_vgfmag", @@ -5873,39 +6039,55 @@ match name { "llvm.s390.vlrl" => "__builtin_s390_vlrlr", "llvm.s390.vmaeb" => "__builtin_s390_vmaeb", "llvm.s390.vmaef" => "__builtin_s390_vmaef", + "llvm.s390.vmaeg" => "__builtin_s390_vmaeg", "llvm.s390.vmaeh" => "__builtin_s390_vmaeh", "llvm.s390.vmahb" => "__builtin_s390_vmahb", "llvm.s390.vmahf" => "__builtin_s390_vmahf", + "llvm.s390.vmahg" => "__builtin_s390_vmahg", "llvm.s390.vmahh" => "__builtin_s390_vmahh", + "llvm.s390.vmahq" => "__builtin_s390_vmahq", "llvm.s390.vmaleb" => "__builtin_s390_vmaleb", "llvm.s390.vmalef" => "__builtin_s390_vmalef", + "llvm.s390.vmaleg" => "__builtin_s390_vmaleg", "llvm.s390.vmaleh" => "__builtin_s390_vmaleh", "llvm.s390.vmalhb" => "__builtin_s390_vmalhb", "llvm.s390.vmalhf" => "__builtin_s390_vmalhf", + "llvm.s390.vmalhg" => "__builtin_s390_vmalhg", "llvm.s390.vmalhh" => "__builtin_s390_vmalhh", + "llvm.s390.vmalhq" => "__builtin_s390_vmalhq", "llvm.s390.vmalob" => "__builtin_s390_vmalob", "llvm.s390.vmalof" => "__builtin_s390_vmalof", + "llvm.s390.vmalog" => "__builtin_s390_vmalog", "llvm.s390.vmaloh" => "__builtin_s390_vmaloh", "llvm.s390.vmaob" => "__builtin_s390_vmaob", "llvm.s390.vmaof" => "__builtin_s390_vmaof", + "llvm.s390.vmaog" => "__builtin_s390_vmaog", "llvm.s390.vmaoh" => "__builtin_s390_vmaoh", "llvm.s390.vmeb" => "__builtin_s390_vmeb", "llvm.s390.vmef" => "__builtin_s390_vmef", + "llvm.s390.vmeg" => "__builtin_s390_vmeg", "llvm.s390.vmeh" => "__builtin_s390_vmeh", "llvm.s390.vmhb" => "__builtin_s390_vmhb", "llvm.s390.vmhf" => "__builtin_s390_vmhf", + "llvm.s390.vmhg" => "__builtin_s390_vmhg", "llvm.s390.vmhh" => "__builtin_s390_vmhh", + "llvm.s390.vmhq" => "__builtin_s390_vmhq", "llvm.s390.vmleb" => "__builtin_s390_vmleb", "llvm.s390.vmlef" => "__builtin_s390_vmlef", + "llvm.s390.vmleg" => "__builtin_s390_vmleg", "llvm.s390.vmleh" => "__builtin_s390_vmleh", "llvm.s390.vmlhb" => "__builtin_s390_vmlhb", "llvm.s390.vmlhf" => "__builtin_s390_vmlhf", + "llvm.s390.vmlhg" => "__builtin_s390_vmlhg", "llvm.s390.vmlhh" => "__builtin_s390_vmlhh", + "llvm.s390.vmlhq" => "__builtin_s390_vmlhq", "llvm.s390.vmlob" => "__builtin_s390_vmlob", "llvm.s390.vmlof" => "__builtin_s390_vmlof", + "llvm.s390.vmlog" => "__builtin_s390_vmlog", "llvm.s390.vmloh" => "__builtin_s390_vmloh", "llvm.s390.vmob" => "__builtin_s390_vmob", "llvm.s390.vmof" => "__builtin_s390_vmof", + "llvm.s390.vmog" => "__builtin_s390_vmog", "llvm.s390.vmoh" => "__builtin_s390_vmoh", "llvm.s390.vmslg" => "__builtin_s390_vmslg", "llvm.s390.vpdi" => "__builtin_s390_vpdi", @@ -5950,18 +6132,20 @@ match name { "llvm.s390.vtm" => "__builtin_s390_vtm", "llvm.s390.vuphb" => "__builtin_s390_vuphb", "llvm.s390.vuphf" => "__builtin_s390_vuphf", + "llvm.s390.vuphg" => "__builtin_s390_vuphg", "llvm.s390.vuphh" => "__builtin_s390_vuphh", "llvm.s390.vuplb" => "__builtin_s390_vuplb", "llvm.s390.vuplf" => "__builtin_s390_vuplf", + "llvm.s390.vuplg" => "__builtin_s390_vuplg", "llvm.s390.vuplhb" => "__builtin_s390_vuplhb", "llvm.s390.vuplhf" => "__builtin_s390_vuplhf", + "llvm.s390.vuplhg" => "__builtin_s390_vuplhg", "llvm.s390.vuplhh" => "__builtin_s390_vuplhh", "llvm.s390.vuplhw" => "__builtin_s390_vuplhw", "llvm.s390.vupllb" => "__builtin_s390_vupllb", "llvm.s390.vupllf" => "__builtin_s390_vupllf", + "llvm.s390.vupllg" => "__builtin_s390_vupllg", "llvm.s390.vupllh" => "__builtin_s390_vupllh", - // spv - "llvm.spv.create.handle" => "__builtin_hlsl_create_handle", // ve "llvm.ve.vl.andm.MMM" => "__builtin_ve_vl_andm_MMM", "llvm.ve.vl.andm.mmm" => "__builtin_ve_vl_andm_mmm", @@ -7328,6 +7512,27 @@ match name { "llvm.x86.avx.vtestz.ps.256" => "__builtin_ia32_vtestzps256", "llvm.x86.avx.vzeroall" => "__builtin_ia32_vzeroall", "llvm.x86.avx.vzeroupper" => "__builtin_ia32_vzeroupper", + "llvm.x86.avx10.mask.getexp.bf16.128" => "__builtin_ia32_vgetexpbf16128_mask", + "llvm.x86.avx10.mask.getexp.bf16.256" => "__builtin_ia32_vgetexpbf16256_mask", + "llvm.x86.avx10.mask.getexp.bf16.512" => "__builtin_ia32_vgetexpbf16512_mask", + "llvm.x86.avx10.mask.getmant.bf16.128" => "__builtin_ia32_vgetmantbf16128_mask", + "llvm.x86.avx10.mask.getmant.bf16.256" => "__builtin_ia32_vgetmantbf16256_mask", + "llvm.x86.avx10.mask.getmant.bf16.512" => "__builtin_ia32_vgetmantbf16512_mask", + "llvm.x86.avx10.mask.rcp.bf16.128" => "__builtin_ia32_vrcpbf16128_mask", + "llvm.x86.avx10.mask.rcp.bf16.256" => "__builtin_ia32_vrcpbf16256_mask", + "llvm.x86.avx10.mask.rcp.bf16.512" => "__builtin_ia32_vrcpbf16512_mask", + "llvm.x86.avx10.mask.reduce.bf16.128" => "__builtin_ia32_vreducebf16128_mask", + "llvm.x86.avx10.mask.reduce.bf16.256" => "__builtin_ia32_vreducebf16256_mask", + "llvm.x86.avx10.mask.reduce.bf16.512" => "__builtin_ia32_vreducebf16512_mask", + "llvm.x86.avx10.mask.rndscale.bf16.128" => "__builtin_ia32_vrndscalebf16_128_mask", + "llvm.x86.avx10.mask.rndscale.bf16.256" => "__builtin_ia32_vrndscalebf16_256_mask", + "llvm.x86.avx10.mask.rndscale.bf16.512" => "__builtin_ia32_vrndscalebf16_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.128" => "__builtin_ia32_vrsqrtbf16128_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.256" => "__builtin_ia32_vrsqrtbf16256_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.512" => "__builtin_ia32_vrsqrtbf16512_mask", + "llvm.x86.avx10.mask.scalef.bf16.128" => "__builtin_ia32_vscalefbf16128_mask", + "llvm.x86.avx10.mask.scalef.bf16.256" => "__builtin_ia32_vscalefbf16256_mask", + "llvm.x86.avx10.mask.scalef.bf16.512" => "__builtin_ia32_vscalefbf16512_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.128" => "__builtin_ia32_vcvt2ps2phx128_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.256" => "__builtin_ia32_vcvt2ps2phx256_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.512" => "__builtin_ia32_vcvt2ps2phx512_mask", @@ -7346,171 +7551,194 @@ match name { "llvm.x86.avx10.mask.vcvthf82ph128" => "__builtin_ia32_vcvthf8_2ph128_mask", "llvm.x86.avx10.mask.vcvthf82ph256" => "__builtin_ia32_vcvthf8_2ph256_mask", "llvm.x86.avx10.mask.vcvthf82ph512" => "__builtin_ia32_vcvthf8_2ph512_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8128" => "__builtin_ia32_vcvtneph2bf8_128_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8256" => "__builtin_ia32_vcvtneph2bf8_256_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8512" => "__builtin_ia32_vcvtneph2bf8_512_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s128" => "__builtin_ia32_vcvtneph2bf8s_128_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s256" => "__builtin_ia32_vcvtneph2bf8s_256_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s512" => "__builtin_ia32_vcvtneph2bf8s_512_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8128" => "__builtin_ia32_vcvtneph2hf8_128_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8256" => "__builtin_ia32_vcvtneph2hf8_256_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8512" => "__builtin_ia32_vcvtneph2hf8_512_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s128" => "__builtin_ia32_vcvtneph2hf8s_128_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s256" => "__builtin_ia32_vcvtneph2hf8s_256_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s512" => "__builtin_ia32_vcvtneph2hf8s_512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2dq256" => "__builtin_ia32_vcvtpd2dq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ph256" => "__builtin_ia32_vcvtpd2ph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ps256" => "__builtin_ia32_vcvtpd2ps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2qq256" => "__builtin_ia32_vcvtpd2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2udq256" => "__builtin_ia32_vcvtpd2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2uqq256" => "__builtin_ia32_vcvtpd2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2dq256" => "__builtin_ia32_vcvtph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvtph2bf8128" => "__builtin_ia32_vcvtph2bf8_128_mask", + "llvm.x86.avx10.mask.vcvtph2bf8256" => "__builtin_ia32_vcvtph2bf8_256_mask", + "llvm.x86.avx10.mask.vcvtph2bf8512" => "__builtin_ia32_vcvtph2bf8_512_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s128" => "__builtin_ia32_vcvtph2bf8s_128_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s256" => "__builtin_ia32_vcvtph2bf8s_256_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s512" => "__builtin_ia32_vcvtph2bf8s_512_mask", + "llvm.x86.avx10.mask.vcvtph2hf8128" => "__builtin_ia32_vcvtph2hf8_128_mask", + "llvm.x86.avx10.mask.vcvtph2hf8256" => "__builtin_ia32_vcvtph2hf8_256_mask", + "llvm.x86.avx10.mask.vcvtph2hf8512" => "__builtin_ia32_vcvtph2hf8_512_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s128" => "__builtin_ia32_vcvtph2hf8s_128_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s256" => "__builtin_ia32_vcvtph2hf8s_256_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s512" => "__builtin_ia32_vcvtph2hf8s_512_mask", "llvm.x86.avx10.mask.vcvtph2ibs128" => "__builtin_ia32_vcvtph2ibs128_mask", "llvm.x86.avx10.mask.vcvtph2ibs256" => "__builtin_ia32_vcvtph2ibs256_mask", "llvm.x86.avx10.mask.vcvtph2ibs512" => "__builtin_ia32_vcvtph2ibs512_mask", "llvm.x86.avx10.mask.vcvtph2iubs128" => "__builtin_ia32_vcvtph2iubs128_mask", "llvm.x86.avx10.mask.vcvtph2iubs256" => "__builtin_ia32_vcvtph2iubs256_mask", "llvm.x86.avx10.mask.vcvtph2iubs512" => "__builtin_ia32_vcvtph2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2pd256" => "__builtin_ia32_vcvtph2pd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2psx256" => "__builtin_ia32_vcvtph2psx256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2qq256" => "__builtin_ia32_vcvtph2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2udq256" => "__builtin_ia32_vcvtph2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uqq256" => "__builtin_ia32_vcvtph2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uw256" => "__builtin_ia32_vcvtph2uw256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2w256" => "__builtin_ia32_vcvtph2w256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2dq256" => "__builtin_ia32_vcvtps2dq256_round_mask", "llvm.x86.avx10.mask.vcvtps2ibs128" => "__builtin_ia32_vcvtps2ibs128_mask", "llvm.x86.avx10.mask.vcvtps2ibs256" => "__builtin_ia32_vcvtps2ibs256_mask", "llvm.x86.avx10.mask.vcvtps2ibs512" => "__builtin_ia32_vcvtps2ibs512_mask", "llvm.x86.avx10.mask.vcvtps2iubs128" => "__builtin_ia32_vcvtps2iubs128_mask", "llvm.x86.avx10.mask.vcvtps2iubs256" => "__builtin_ia32_vcvtps2iubs256_mask", "llvm.x86.avx10.mask.vcvtps2iubs512" => "__builtin_ia32_vcvtps2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2pd256" => "__builtin_ia32_vcvtps2pd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2ph256" => "__builtin_ia32_vcvtps2ph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2phx256" => "__builtin_ia32_vcvtps2phx256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2qq256" => "__builtin_ia32_vcvtps2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2udq256" => "__builtin_ia32_vcvtps2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2uqq256" => "__builtin_ia32_vcvtps2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2dq256" => "__builtin_ia32_vcvttpd2dq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2qq256" => "__builtin_ia32_vcvttpd2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2udq256" => "__builtin_ia32_vcvttpd2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2uqq256" => "__builtin_ia32_vcvttpd2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2dq256" => "__builtin_ia32_vcvttph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttpd2dqs.128" => "__builtin_ia32_vcvttpd2dqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2dqs.256" => "__builtin_ia32_vcvttpd2dqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2dqs.round.512" => "__builtin_ia32_vcvttpd2dqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2qqs.128" => "__builtin_ia32_vcvttpd2qqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2qqs.256" => "__builtin_ia32_vcvttpd2qqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2qqs.round.512" => "__builtin_ia32_vcvttpd2qqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2udqs.128" => "__builtin_ia32_vcvttpd2udqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2udqs.256" => "__builtin_ia32_vcvttpd2udqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2udqs.round.512" => "__builtin_ia32_vcvttpd2udqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2uqqs.128" => "__builtin_ia32_vcvttpd2uqqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2uqqs.256" => "__builtin_ia32_vcvttpd2uqqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2uqqs.round.512" => "__builtin_ia32_vcvttpd2uqqs512_round_mask", "llvm.x86.avx10.mask.vcvttph2ibs128" => "__builtin_ia32_vcvttph2ibs128_mask", "llvm.x86.avx10.mask.vcvttph2ibs256" => "__builtin_ia32_vcvttph2ibs256_mask", "llvm.x86.avx10.mask.vcvttph2ibs512" => "__builtin_ia32_vcvttph2ibs512_mask", "llvm.x86.avx10.mask.vcvttph2iubs128" => "__builtin_ia32_vcvttph2iubs128_mask", "llvm.x86.avx10.mask.vcvttph2iubs256" => "__builtin_ia32_vcvttph2iubs256_mask", "llvm.x86.avx10.mask.vcvttph2iubs512" => "__builtin_ia32_vcvttph2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2qq256" => "__builtin_ia32_vcvttph2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2udq256" => "__builtin_ia32_vcvttph2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uqq256" => "__builtin_ia32_vcvttph2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uw256" => "__builtin_ia32_vcvttph2uw256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2w256" => "__builtin_ia32_vcvttph2w256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2dq256" => "__builtin_ia32_vcvttps2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttps2dqs.128" => "__builtin_ia32_vcvttps2dqs128_mask", + "llvm.x86.avx10.mask.vcvttps2dqs.256" => "__builtin_ia32_vcvttps2dqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2dqs.round.512" => "__builtin_ia32_vcvttps2dqs512_round_mask", "llvm.x86.avx10.mask.vcvttps2ibs128" => "__builtin_ia32_vcvttps2ibs128_mask", "llvm.x86.avx10.mask.vcvttps2ibs256" => "__builtin_ia32_vcvttps2ibs256_mask", "llvm.x86.avx10.mask.vcvttps2ibs512" => "__builtin_ia32_vcvttps2ibs512_mask", "llvm.x86.avx10.mask.vcvttps2iubs128" => "__builtin_ia32_vcvttps2iubs128_mask", "llvm.x86.avx10.mask.vcvttps2iubs256" => "__builtin_ia32_vcvttps2iubs256_mask", "llvm.x86.avx10.mask.vcvttps2iubs512" => "__builtin_ia32_vcvttps2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2qq256" => "__builtin_ia32_vcvttps2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2udq256" => "__builtin_ia32_vcvttps2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2uqq256" => "__builtin_ia32_vcvttps2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_mask3", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmulcph256" => "__builtin_ia32_vfcmulcph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_mask3", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmulcph256" => "__builtin_ia32_vfmulcph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexppd256" => "__builtin_ia32_vgetexppd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpph256" => "__builtin_ia32_vgetexpph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpps256" => "__builtin_ia32_vgetexpps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantpd256" => "__builtin_ia32_vgetmantpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantph256" => "__builtin_ia32_vgetmantph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantps256" => "__builtin_ia32_vgetmantps256_round_mask", + "llvm.x86.avx10.mask.vcvttps2qqs.128" => "__builtin_ia32_vcvttps2qqs128_mask", + "llvm.x86.avx10.mask.vcvttps2qqs.256" => "__builtin_ia32_vcvttps2qqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2qqs.round.512" => "__builtin_ia32_vcvttps2qqs512_round_mask", + "llvm.x86.avx10.mask.vcvttps2udqs.128" => "__builtin_ia32_vcvttps2udqs128_mask", + "llvm.x86.avx10.mask.vcvttps2udqs.256" => "__builtin_ia32_vcvttps2udqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2udqs.round.512" => "__builtin_ia32_vcvttps2udqs512_round_mask", + "llvm.x86.avx10.mask.vcvttps2uqqs.128" => "__builtin_ia32_vcvttps2uqqs128_mask", + "llvm.x86.avx10.mask.vcvttps2uqqs.256" => "__builtin_ia32_vcvttps2uqqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2uqqs.round.512" => "__builtin_ia32_vcvttps2uqqs512_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd.round" => "__builtin_ia32_vminmaxpd512_round_mask", "llvm.x86.avx10.mask.vminmaxpd128" => "__builtin_ia32_vminmaxpd128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd256.round" => "__builtin_ia32_vminmaxpd256_round_mask", + "llvm.x86.avx10.mask.vminmaxpd256" => "__builtin_ia32_vminmaxpd256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph.round" => "__builtin_ia32_vminmaxph512_round_mask", "llvm.x86.avx10.mask.vminmaxph128" => "__builtin_ia32_vminmaxph128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph256.round" => "__builtin_ia32_vminmaxph256_round_mask", + "llvm.x86.avx10.mask.vminmaxph256" => "__builtin_ia32_vminmaxph256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps.round" => "__builtin_ia32_vminmaxps512_round_mask", "llvm.x86.avx10.mask.vminmaxps128" => "__builtin_ia32_vminmaxps128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps256.round" => "__builtin_ia32_vminmaxps256_round_mask", + "llvm.x86.avx10.mask.vminmaxps256" => "__builtin_ia32_vminmaxps256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsd.round" => "__builtin_ia32_vminmaxsd_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsh.round" => "__builtin_ia32_vminmaxsh_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxss.round" => "__builtin_ia32_vminmaxss_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangepd256" => "__builtin_ia32_vrangepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangeps256" => "__builtin_ia32_vrangeps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreducepd256" => "__builtin_ia32_vreducepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceph256" => "__builtin_ia32_vreduceph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceps256" => "__builtin_ia32_vreduceps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscalepd256" => "__builtin_ia32_vrndscalepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleph256" => "__builtin_ia32_vrndscaleph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleps256" => "__builtin_ia32_vrndscaleps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefpd256" => "__builtin_ia32_vscalefpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefph256" => "__builtin_ia32_vscalefph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefps256" => "__builtin_ia32_vscalefps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_maskz", + "llvm.x86.avx10.vaddbf16128" => "__builtin_ia32_vaddbf16128", + "llvm.x86.avx10.vaddbf16256" => "__builtin_ia32_vaddbf16256", + "llvm.x86.avx10.vaddbf16512" => "__builtin_ia32_vaddbf16512", "llvm.x86.avx10.vaddpd256" => "__builtin_ia32_vaddpd256_round", "llvm.x86.avx10.vaddph256" => "__builtin_ia32_vaddph256_round", "llvm.x86.avx10.vaddps256" => "__builtin_ia32_vaddps256_round", - "llvm.x86.avx10.vcvtne2ph2bf8128" => "__builtin_ia32_vcvtne2ph2bf8_128", - "llvm.x86.avx10.vcvtne2ph2bf8256" => "__builtin_ia32_vcvtne2ph2bf8_256", - "llvm.x86.avx10.vcvtne2ph2bf8512" => "__builtin_ia32_vcvtne2ph2bf8_512", - "llvm.x86.avx10.vcvtne2ph2bf8s128" => "__builtin_ia32_vcvtne2ph2bf8s_128", - "llvm.x86.avx10.vcvtne2ph2bf8s256" => "__builtin_ia32_vcvtne2ph2bf8s_256", - "llvm.x86.avx10.vcvtne2ph2bf8s512" => "__builtin_ia32_vcvtne2ph2bf8s_512", - "llvm.x86.avx10.vcvtne2ph2hf8128" => "__builtin_ia32_vcvtne2ph2hf8_128", - "llvm.x86.avx10.vcvtne2ph2hf8256" => "__builtin_ia32_vcvtne2ph2hf8_256", - "llvm.x86.avx10.vcvtne2ph2hf8512" => "__builtin_ia32_vcvtne2ph2hf8_512", - "llvm.x86.avx10.vcvtne2ph2hf8s128" => "__builtin_ia32_vcvtne2ph2hf8s_128", - "llvm.x86.avx10.vcvtne2ph2hf8s256" => "__builtin_ia32_vcvtne2ph2hf8s_256", - "llvm.x86.avx10.vcvtne2ph2hf8s512" => "__builtin_ia32_vcvtne2ph2hf8s_512", - "llvm.x86.avx10.vcvtnebf162ibs128" => "__builtin_ia32_vcvtnebf162ibs128", - "llvm.x86.avx10.vcvtnebf162ibs256" => "__builtin_ia32_vcvtnebf162ibs256", - "llvm.x86.avx10.vcvtnebf162ibs512" => "__builtin_ia32_vcvtnebf162ibs512", - "llvm.x86.avx10.vcvtnebf162iubs128" => "__builtin_ia32_vcvtnebf162iubs128", - "llvm.x86.avx10.vcvtnebf162iubs256" => "__builtin_ia32_vcvtnebf162iubs256", - "llvm.x86.avx10.vcvtnebf162iubs512" => "__builtin_ia32_vcvtnebf162iubs512", - "llvm.x86.avx10.vcvttnebf162ibs128" => "__builtin_ia32_vcvttnebf162ibs128", - "llvm.x86.avx10.vcvttnebf162ibs256" => "__builtin_ia32_vcvttnebf162ibs256", - "llvm.x86.avx10.vcvttnebf162ibs512" => "__builtin_ia32_vcvttnebf162ibs512", - "llvm.x86.avx10.vcvttnebf162iubs128" => "__builtin_ia32_vcvttnebf162iubs128", - "llvm.x86.avx10.vcvttnebf162iubs256" => "__builtin_ia32_vcvttnebf162iubs256", - "llvm.x86.avx10.vcvttnebf162iubs512" => "__builtin_ia32_vcvttnebf162iubs512", - "llvm.x86.avx10.vdivpd256" => "__builtin_ia32_vdivpd256_round", - "llvm.x86.avx10.vdivph256" => "__builtin_ia32_vdivph256_round", - "llvm.x86.avx10.vdivps256" => "__builtin_ia32_vdivps256_round", + "llvm.x86.avx10.vcomisbf16eq" => "__builtin_ia32_vcomisbf16eq", + "llvm.x86.avx10.vcomisbf16ge" => "__builtin_ia32_vcomisbf16ge", + "llvm.x86.avx10.vcomisbf16gt" => "__builtin_ia32_vcomisbf16gt", + "llvm.x86.avx10.vcomisbf16le" => "__builtin_ia32_vcomisbf16le", + "llvm.x86.avx10.vcomisbf16lt" => "__builtin_ia32_vcomisbf16lt", + "llvm.x86.avx10.vcomisbf16neq" => "__builtin_ia32_vcomisbf16neq", + "llvm.x86.avx10.vcvt2ph2bf8128" => "__builtin_ia32_vcvt2ph2bf8_128", + "llvm.x86.avx10.vcvt2ph2bf8256" => "__builtin_ia32_vcvt2ph2bf8_256", + "llvm.x86.avx10.vcvt2ph2bf8512" => "__builtin_ia32_vcvt2ph2bf8_512", + "llvm.x86.avx10.vcvt2ph2bf8s128" => "__builtin_ia32_vcvt2ph2bf8s_128", + "llvm.x86.avx10.vcvt2ph2bf8s256" => "__builtin_ia32_vcvt2ph2bf8s_256", + "llvm.x86.avx10.vcvt2ph2bf8s512" => "__builtin_ia32_vcvt2ph2bf8s_512", + "llvm.x86.avx10.vcvt2ph2hf8128" => "__builtin_ia32_vcvt2ph2hf8_128", + "llvm.x86.avx10.vcvt2ph2hf8256" => "__builtin_ia32_vcvt2ph2hf8_256", + "llvm.x86.avx10.vcvt2ph2hf8512" => "__builtin_ia32_vcvt2ph2hf8_512", + "llvm.x86.avx10.vcvt2ph2hf8s128" => "__builtin_ia32_vcvt2ph2hf8s_128", + "llvm.x86.avx10.vcvt2ph2hf8s256" => "__builtin_ia32_vcvt2ph2hf8s_256", + "llvm.x86.avx10.vcvt2ph2hf8s512" => "__builtin_ia32_vcvt2ph2hf8s_512", + "llvm.x86.avx10.vcvtbf162ibs128" => "__builtin_ia32_vcvtbf162ibs128", + "llvm.x86.avx10.vcvtbf162ibs256" => "__builtin_ia32_vcvtbf162ibs256", + "llvm.x86.avx10.vcvtbf162ibs512" => "__builtin_ia32_vcvtbf162ibs512", + "llvm.x86.avx10.vcvtbf162iubs128" => "__builtin_ia32_vcvtbf162iubs128", + "llvm.x86.avx10.vcvtbf162iubs256" => "__builtin_ia32_vcvtbf162iubs256", + "llvm.x86.avx10.vcvtbf162iubs512" => "__builtin_ia32_vcvtbf162iubs512", + "llvm.x86.avx10.vcvttbf162ibs128" => "__builtin_ia32_vcvttbf162ibs128", + "llvm.x86.avx10.vcvttbf162ibs256" => "__builtin_ia32_vcvttbf162ibs256", + "llvm.x86.avx10.vcvttbf162ibs512" => "__builtin_ia32_vcvttbf162ibs512", + "llvm.x86.avx10.vcvttbf162iubs128" => "__builtin_ia32_vcvttbf162iubs128", + "llvm.x86.avx10.vcvttbf162iubs256" => "__builtin_ia32_vcvttbf162iubs256", + "llvm.x86.avx10.vcvttbf162iubs512" => "__builtin_ia32_vcvttbf162iubs512", + "llvm.x86.avx10.vcvttsd2sis" => "__builtin_ia32_vcvttsd2sis32", + "llvm.x86.avx10.vcvttsd2sis64" => "__builtin_ia32_vcvttsd2sis64", + "llvm.x86.avx10.vcvttsd2usis" => "__builtin_ia32_vcvttsd2usis32", + "llvm.x86.avx10.vcvttsd2usis64" => "__builtin_ia32_vcvttsd2usis64", + "llvm.x86.avx10.vcvttss2sis" => "__builtin_ia32_vcvttss2sis32", + "llvm.x86.avx10.vcvttss2sis64" => "__builtin_ia32_vcvttss2sis64", + "llvm.x86.avx10.vcvttss2usis" => "__builtin_ia32_vcvttss2usis32", + "llvm.x86.avx10.vcvttss2usis64" => "__builtin_ia32_vcvttss2usis64", + "llvm.x86.avx10.vdivbf16128" => "__builtin_ia32_vdivbf16128", + "llvm.x86.avx10.vdivbf16256" => "__builtin_ia32_vdivbf16256", + "llvm.x86.avx10.vdivbf16512" => "__builtin_ia32_vdivbf16512", "llvm.x86.avx10.vdpphps.128" => "__builtin_ia32_vdpphps128", "llvm.x86.avx10.vdpphps.256" => "__builtin_ia32_vdpphps256", "llvm.x86.avx10.vdpphps.512" => "__builtin_ia32_vdpphps512", - "llvm.x86.avx10.vfmaddsubpd256" => "__builtin_ia32_vfmaddsubpd256_round", - "llvm.x86.avx10.vfmaddsubph256" => "__builtin_ia32_vfmaddsubph256_round", - "llvm.x86.avx10.vfmaddsubps256" => "__builtin_ia32_vfmaddsubps256_round", - "llvm.x86.avx10.vmaxpd256" => "__builtin_ia32_vmaxpd256_round", - "llvm.x86.avx10.vmaxph256" => "__builtin_ia32_vmaxph256_round", - "llvm.x86.avx10.vmaxps256" => "__builtin_ia32_vmaxps256_round", - "llvm.x86.avx10.vminmaxnepbf16128" => "__builtin_ia32_vminmaxnepbf16128", - "llvm.x86.avx10.vminmaxnepbf16256" => "__builtin_ia32_vminmaxnepbf16256", - "llvm.x86.avx10.vminmaxnepbf16512" => "__builtin_ia32_vminmaxnepbf16512", + "llvm.x86.avx10.vfmadd132bf16128" => "__builtin_ia32_vfmadd132bf16128", + "llvm.x86.avx10.vfmadd132bf16256" => "__builtin_ia32_vfmadd132bf16256", + "llvm.x86.avx10.vfmadd132bf16512" => "__builtin_ia32_vfmadd132bf16512", + "llvm.x86.avx10.vfmadd213bf16128" => "__builtin_ia32_vfmadd213bf16128", + "llvm.x86.avx10.vfmadd213bf16256" => "__builtin_ia32_vfmadd213bf16256", + "llvm.x86.avx10.vfmadd231bf16128" => "__builtin_ia32_vfmadd231bf16128", + "llvm.x86.avx10.vfmadd231bf16256" => "__builtin_ia32_vfmadd231bf16256", + "llvm.x86.avx10.vfmadd231bf16512" => "__builtin_ia32_vfmadd231bf16512", + "llvm.x86.avx10.vfmsub132bf16128" => "__builtin_ia32_vfmsub132bf16128", + "llvm.x86.avx10.vfmsub132bf16256" => "__builtin_ia32_vfmsub132bf16256", + "llvm.x86.avx10.vfmsub132bf16512" => "__builtin_ia32_vfmsub132bf16512", + "llvm.x86.avx10.vfmsub213bf16128" => "__builtin_ia32_vfmsub213bf16128", + "llvm.x86.avx10.vfmsub213bf16256" => "__builtin_ia32_vfmsub213bf16256", + "llvm.x86.avx10.vfmsub213bf16512" => "__builtin_ia32_vfmsub213bf16512", + "llvm.x86.avx10.vfmsub231bf16128" => "__builtin_ia32_vfmsub231bf16128", + "llvm.x86.avx10.vfmsub231bf16256" => "__builtin_ia32_vfmsub231bf16256", + "llvm.x86.avx10.vfmsub231bf16512" => "__builtin_ia32_vfmsub231bf16512", + "llvm.x86.avx10.vfnmadd132bf16128" => "__builtin_ia32_vfnmadd132bf16128", + "llvm.x86.avx10.vfnmadd132bf16256" => "__builtin_ia32_vfnmadd132bf16256", + "llvm.x86.avx10.vfnmadd132bf16512" => "__builtin_ia32_vfnmadd132bf16512", + "llvm.x86.avx10.vfnmadd213bf16128" => "__builtin_ia32_vfnmadd213bf16128", + "llvm.x86.avx10.vfnmadd213bf16256" => "__builtin_ia32_vfnmadd213bf16256", + "llvm.x86.avx10.vfnmadd213bf16512" => "__builtin_ia32_vfnmadd213bf16512", + "llvm.x86.avx10.vfnmadd231bf16128" => "__builtin_ia32_vfnmadd231bf16128", + "llvm.x86.avx10.vfnmadd231bf16256" => "__builtin_ia32_vfnmadd231bf16256", + "llvm.x86.avx10.vfnmadd231bf16512" => "__builtin_ia32_vfnmadd231bf16512", + "llvm.x86.avx10.vfnmsub132bf16128" => "__builtin_ia32_vfnmsub132bf16128", + "llvm.x86.avx10.vfnmsub132bf16256" => "__builtin_ia32_vfnmsub132bf16256", + "llvm.x86.avx10.vfnmsub132bf16512" => "__builtin_ia32_vfnmsub132bf16512", + "llvm.x86.avx10.vfnmsub213bf16128" => "__builtin_ia32_vfnmsub213bf16128", + "llvm.x86.avx10.vfnmsub213bf16256" => "__builtin_ia32_vfnmsub213bf16256", + "llvm.x86.avx10.vfnmsub213bf16512" => "__builtin_ia32_vfnmsub213bf16512", + "llvm.x86.avx10.vfnmsub231bf16128" => "__builtin_ia32_vfnmsub231bf16128", + "llvm.x86.avx10.vfnmsub231bf16256" => "__builtin_ia32_vfnmsub231bf16256", + "llvm.x86.avx10.vfnmsub231bf16512" => "__builtin_ia32_vfnmsub231bf16512", + "llvm.x86.avx10.vmaxbf16128" => "__builtin_ia32_vmaxbf16128", + "llvm.x86.avx10.vmaxbf16256" => "__builtin_ia32_vmaxbf16256", + "llvm.x86.avx10.vmaxbf16512" => "__builtin_ia32_vmaxbf16512", + "llvm.x86.avx10.vminbf16128" => "__builtin_ia32_vminbf16128", + "llvm.x86.avx10.vminbf16256" => "__builtin_ia32_vminbf16256", + "llvm.x86.avx10.vminbf16512" => "__builtin_ia32_vminbf16512", + "llvm.x86.avx10.vminmaxbf16128" => "__builtin_ia32_vminmaxbf16128", + "llvm.x86.avx10.vminmaxbf16256" => "__builtin_ia32_vminmaxbf16256", + "llvm.x86.avx10.vminmaxbf16512" => "__builtin_ia32_vminmaxbf16512", "llvm.x86.avx10.vminmaxpd128" => "__builtin_ia32_vminmaxpd128", "llvm.x86.avx10.vminmaxpd256" => "__builtin_ia32_vminmaxpd256", "llvm.x86.avx10.vminmaxph128" => "__builtin_ia32_vminmaxph128", "llvm.x86.avx10.vminmaxph256" => "__builtin_ia32_vminmaxph256", "llvm.x86.avx10.vminmaxps128" => "__builtin_ia32_vminmaxps128", "llvm.x86.avx10.vminmaxps256" => "__builtin_ia32_vminmaxps256", - "llvm.x86.avx10.vminpd256" => "__builtin_ia32_vminpd256_round", - "llvm.x86.avx10.vminph256" => "__builtin_ia32_vminph256_round", - "llvm.x86.avx10.vminps256" => "__builtin_ia32_vminps256_round", + "llvm.x86.avx10.vmovrsb128" => "__builtin_ia32_vmovrsb128", + "llvm.x86.avx10.vmovrsb256" => "__builtin_ia32_vmovrsb256", + "llvm.x86.avx10.vmovrsb512" => "__builtin_ia32_vmovrsb512", + "llvm.x86.avx10.vmovrsd128" => "__builtin_ia32_vmovrsd128", + "llvm.x86.avx10.vmovrsd256" => "__builtin_ia32_vmovrsd256", + "llvm.x86.avx10.vmovrsd512" => "__builtin_ia32_vmovrsd512", + "llvm.x86.avx10.vmovrsq128" => "__builtin_ia32_vmovrsq128", + "llvm.x86.avx10.vmovrsq256" => "__builtin_ia32_vmovrsq256", + "llvm.x86.avx10.vmovrsq512" => "__builtin_ia32_vmovrsq512", + "llvm.x86.avx10.vmovrsw128" => "__builtin_ia32_vmovrsw128", + "llvm.x86.avx10.vmovrsw256" => "__builtin_ia32_vmovrsw256", + "llvm.x86.avx10.vmovrsw512" => "__builtin_ia32_vmovrsw512", "llvm.x86.avx10.vmpsadbw.512" => "__builtin_ia32_mpsadbw512", - "llvm.x86.avx10.vmulpd256" => "__builtin_ia32_vmulpd256_round", - "llvm.x86.avx10.vmulph256" => "__builtin_ia32_vmulph256_round", - "llvm.x86.avx10.vmulps256" => "__builtin_ia32_vmulps256_round", + "llvm.x86.avx10.vmulbf16128" => "__builtin_ia32_vmulbf16128", + "llvm.x86.avx10.vmulbf16256" => "__builtin_ia32_vmulbf16256", + "llvm.x86.avx10.vmulbf16512" => "__builtin_ia32_vmulbf16512", "llvm.x86.avx10.vpdpbssd.512" => "__builtin_ia32_vpdpbssd512", "llvm.x86.avx10.vpdpbssds.512" => "__builtin_ia32_vpdpbssds512", "llvm.x86.avx10.vpdpbsud.512" => "__builtin_ia32_vpdpbsud512", @@ -7523,12 +7751,9 @@ match name { "llvm.x86.avx10.vpdpwusds.512" => "__builtin_ia32_vpdpwusds512", "llvm.x86.avx10.vpdpwuud.512" => "__builtin_ia32_vpdpwuud512", "llvm.x86.avx10.vpdpwuuds.512" => "__builtin_ia32_vpdpwuuds512", - "llvm.x86.avx10.vsqrtpd256" => "__builtin_ia32_vsqrtpd256_round", - "llvm.x86.avx10.vsqrtph256" => "__builtin_ia32_vsqrtph256_round", - "llvm.x86.avx10.vsqrtps256" => "__builtin_ia32_vsqrtps256_round", - "llvm.x86.avx10.vsubpd256" => "__builtin_ia32_vsubpd256_round", - "llvm.x86.avx10.vsubph256" => "__builtin_ia32_vsubph256_round", - "llvm.x86.avx10.vsubps256" => "__builtin_ia32_vsubps256_round", + "llvm.x86.avx10.vsubbf16128" => "__builtin_ia32_vsubbf16128", + "llvm.x86.avx10.vsubbf16256" => "__builtin_ia32_vsubbf16256", + "llvm.x86.avx10.vsubbf16512" => "__builtin_ia32_vsubbf16512", "llvm.x86.avx2.gather.d.d" => "__builtin_ia32_gatherd_d", "llvm.x86.avx2.gather.d.d.256" => "__builtin_ia32_gatherd_d256", "llvm.x86.avx2.gather.d.pd" => "__builtin_ia32_gatherd_pd", @@ -9279,10 +9504,15 @@ match name { "llvm.x86.mmx.femms" => "__builtin_ia32_femms", "llvm.x86.monitorx" => "__builtin_ia32_monitorx", "llvm.x86.movdir64b" => "__builtin_ia32_movdir64b", + "llvm.x86.movrsdi" => "__builtin_ia32_movrsdi", + "llvm.x86.movrshi" => "__builtin_ia32_movrshi", + "llvm.x86.movrsqi" => "__builtin_ia32_movrsqi", + "llvm.x86.movrssi" => "__builtin_ia32_movrssi", "llvm.x86.mwaitx" => "__builtin_ia32_mwaitx", "llvm.x86.pclmulqdq" => "__builtin_ia32_pclmulqdq128", "llvm.x86.pclmulqdq.256" => "__builtin_ia32_pclmulqdq256", "llvm.x86.pclmulqdq.512" => "__builtin_ia32_pclmulqdq512", + "llvm.x86.prefetchrs" => "__builtin_ia32_prefetchrs", "llvm.x86.ptwrite32" => "__builtin_ia32_ptwrite32", "llvm.x86.ptwrite64" => "__builtin_ia32_ptwrite64", "llvm.x86.rdfsbase.32" => "__builtin_ia32_rdfsbase32", @@ -9536,14 +9766,40 @@ match name { "llvm.x86.stui" => "__builtin_ia32_stui", "llvm.x86.subborrow.u32" => "__builtin_ia32_subborrow_u32", "llvm.x86.subborrow.u64" => "__builtin_ia32_subborrow_u64", + "llvm.x86.t2rpntlvwz0" => "__builtin_ia32_t2rpntlvwz0", + "llvm.x86.t2rpntlvwz0rs" => "__builtin_ia32_t2rpntlvwz0rs", + "llvm.x86.t2rpntlvwz0rst1" => "__builtin_ia32_t2rpntlvwz0rst1", + "llvm.x86.t2rpntlvwz0t1" => "__builtin_ia32_t2rpntlvwz0t1", + "llvm.x86.t2rpntlvwz1" => "__builtin_ia32_t2rpntlvwz1", + "llvm.x86.t2rpntlvwz1rs" => "__builtin_ia32_t2rpntlvwz1rs", + "llvm.x86.t2rpntlvwz1rst1" => "__builtin_ia32_t2rpntlvwz1rst1", + "llvm.x86.t2rpntlvwz1t1" => "__builtin_ia32_t2rpntlvwz1t1", "llvm.x86.tbm.bextri.u32" => "__builtin_ia32_bextri_u32", "llvm.x86.tbm.bextri.u64" => "__builtin_ia32_bextri_u64", "llvm.x86.tcmmimfp16ps" => "__builtin_ia32_tcmmimfp16ps", "llvm.x86.tcmmimfp16ps.internal" => "__builtin_ia32_tcmmimfp16ps_internal", "llvm.x86.tcmmrlfp16ps" => "__builtin_ia32_tcmmrlfp16ps", "llvm.x86.tcmmrlfp16ps.internal" => "__builtin_ia32_tcmmrlfp16ps_internal", + "llvm.x86.tconjtcmmimfp16ps" => "__builtin_ia32_tconjtcmmimfp16ps", + "llvm.x86.tconjtcmmimfp16ps.internal" => "__builtin_ia32_tconjtcmmimfp16ps_internal", + "llvm.x86.tconjtfp16" => "__builtin_ia32_tconjtfp16", + "llvm.x86.tconjtfp16.internal" => "__builtin_ia32_tconjtfp16_internal", + "llvm.x86.tcvtrowd2ps" => "__builtin_ia32_tcvtrowd2ps", + "llvm.x86.tcvtrowd2ps.internal" => "__builtin_ia32_tcvtrowd2ps_internal", + "llvm.x86.tcvtrowps2bf16h" => "__builtin_ia32_tcvtrowps2bf16h", + "llvm.x86.tcvtrowps2bf16h.internal" => "__builtin_ia32_tcvtrowps2bf16h_internal", + "llvm.x86.tcvtrowps2bf16l" => "__builtin_ia32_tcvtrowps2bf16l", + "llvm.x86.tcvtrowps2bf16l.internal" => "__builtin_ia32_tcvtrowps2bf16l_internal", + "llvm.x86.tcvtrowps2phh" => "__builtin_ia32_tcvtrowps2phh", + "llvm.x86.tcvtrowps2phh.internal" => "__builtin_ia32_tcvtrowps2phh_internal", + "llvm.x86.tcvtrowps2phl" => "__builtin_ia32_tcvtrowps2phl", + "llvm.x86.tcvtrowps2phl.internal" => "__builtin_ia32_tcvtrowps2phl_internal", "llvm.x86.tdpbf16ps" => "__builtin_ia32_tdpbf16ps", "llvm.x86.tdpbf16ps.internal" => "__builtin_ia32_tdpbf16ps_internal", + "llvm.x86.tdpbf8ps" => "__builtin_ia32_tdpbf8ps", + "llvm.x86.tdpbf8ps.internal" => "__builtin_ia32_tdpbf8ps_internal", + "llvm.x86.tdpbhf8ps" => "__builtin_ia32_tdpbhf8ps", + "llvm.x86.tdpbhf8ps.internal" => "__builtin_ia32_tdpbhf8ps_internal", "llvm.x86.tdpbssd" => "__builtin_ia32_tdpbssd", "llvm.x86.tdpbssd.internal" => "__builtin_ia32_tdpbssd_internal", "llvm.x86.tdpbsud" => "__builtin_ia32_tdpbsud", @@ -9554,17 +9810,41 @@ match name { "llvm.x86.tdpbuud.internal" => "__builtin_ia32_tdpbuud_internal", "llvm.x86.tdpfp16ps" => "__builtin_ia32_tdpfp16ps", "llvm.x86.tdpfp16ps.internal" => "__builtin_ia32_tdpfp16ps_internal", + "llvm.x86.tdphbf8ps" => "__builtin_ia32_tdphbf8ps", + "llvm.x86.tdphbf8ps.internal" => "__builtin_ia32_tdphbf8ps_internal", + "llvm.x86.tdphf8ps" => "__builtin_ia32_tdphf8ps", + "llvm.x86.tdphf8ps.internal" => "__builtin_ia32_tdphf8ps_internal", "llvm.x86.testui" => "__builtin_ia32_testui", "llvm.x86.tileloadd64" => "__builtin_ia32_tileloadd64", "llvm.x86.tileloadd64.internal" => "__builtin_ia32_tileloadd64_internal", + "llvm.x86.tileloaddrs64" => "__builtin_ia32_tileloaddrs64", + "llvm.x86.tileloaddrs64.internal" => "__builtin_ia32_tileloaddrs64_internal", + "llvm.x86.tileloaddrst164" => "__builtin_ia32_tileloaddrst164", + "llvm.x86.tileloaddrst164.internal" => "__builtin_ia32_tileloaddrst164_internal", "llvm.x86.tileloaddt164" => "__builtin_ia32_tileloaddt164", "llvm.x86.tileloaddt164.internal" => "__builtin_ia32_tileloaddt164_internal", + "llvm.x86.tilemovrow" => "__builtin_ia32_tilemovrow", + "llvm.x86.tilemovrow.internal" => "__builtin_ia32_tilemovrow_internal", "llvm.x86.tilerelease" => "__builtin_ia32_tilerelease", "llvm.x86.tilestored64" => "__builtin_ia32_tilestored64", "llvm.x86.tilestored64.internal" => "__builtin_ia32_tilestored64_internal", "llvm.x86.tilezero" => "__builtin_ia32_tilezero", "llvm.x86.tilezero.internal" => "__builtin_ia32_tilezero_internal", + "llvm.x86.tmmultf32ps" => "__builtin_ia32_tmmultf32ps", + "llvm.x86.tmmultf32ps.internal" => "__builtin_ia32_tmmultf32ps_internal", "llvm.x86.tpause" => "__builtin_ia32_tpause", + "llvm.x86.ttcmmimfp16ps" => "__builtin_ia32_ttcmmimfp16ps", + "llvm.x86.ttcmmimfp16ps.internal" => "__builtin_ia32_ttcmmimfp16ps_internal", + "llvm.x86.ttcmmrlfp16ps" => "__builtin_ia32_ttcmmrlfp16ps", + "llvm.x86.ttcmmrlfp16ps.internal" => "__builtin_ia32_ttcmmrlfp16ps_internal", + "llvm.x86.ttdpbf16ps" => "__builtin_ia32_ttdpbf16ps", + "llvm.x86.ttdpbf16ps.internal" => "__builtin_ia32_ttdpbf16ps_internal", + "llvm.x86.ttdpfp16ps" => "__builtin_ia32_ttdpfp16ps", + "llvm.x86.ttdpfp16ps.internal" => "__builtin_ia32_ttdpfp16ps_internal", + "llvm.x86.ttmmultf32ps" => "__builtin_ia32_ttmmultf32ps", + "llvm.x86.ttmmultf32ps.internal" => "__builtin_ia32_ttmmultf32ps_internal", + "llvm.x86.ttransposed" => "__builtin_ia32_ttransposed", + "llvm.x86.ttransposed.internal" => "__builtin_ia32_ttransposed_internal", "llvm.x86.umonitor" => "__builtin_ia32_umonitor", "llvm.x86.umwait" => "__builtin_ia32_umwait", "llvm.x86.urdmsr" => "__builtin_ia32_urdmsr", @@ -9604,8 +9884,10 @@ match name { "llvm.x86.vsm3rnds2" => "__builtin_ia32_vsm3rnds2", "llvm.x86.vsm4key4128" => "__builtin_ia32_vsm4key4128", "llvm.x86.vsm4key4256" => "__builtin_ia32_vsm4key4256", + "llvm.x86.vsm4key4512" => "__builtin_ia32_vsm4key4512", "llvm.x86.vsm4rnds4128" => "__builtin_ia32_vsm4rnds4128", "llvm.x86.vsm4rnds4256" => "__builtin_ia32_vsm4rnds4256", + "llvm.x86.vsm4rnds4512" => "__builtin_ia32_vsm4rnds4512", "llvm.x86.wbinvd" => "__builtin_ia32_wbinvd", "llvm.x86.wbnoinvd" => "__builtin_ia32_wbnoinvd", "llvm.x86.wrfsbase.32" => "__builtin_ia32_wrfsbase32", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 9caceca92957..ba65c8205a50 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -4,9 +4,7 @@ mod simd; #[cfg(feature = "master")] use std::iter; -#[cfg(feature = "master")] -use gccjit::FunctionType; -use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp}; +use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; #[cfg(feature = "master")] use rustc_abi::ExternAbi; use rustc_abi::{BackendRepr, HasDataLayout}; @@ -132,6 +130,72 @@ fn get_simple_intrinsic<'gcc, 'tcx>( Some(cx.context.get_builtin_function(gcc_name)) } +// TODO(antoyo): We can probably remove these and use the fallback intrinsic implementation. +fn get_simple_function<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + name: Symbol, +) -> Option> { + let (return_type, parameters, func_name) = match name { + sym::minimumf32 => { + let parameters = [ + cx.context.new_parameter(None, cx.float_type, "a"), + cx.context.new_parameter(None, cx.float_type, "b"), + ]; + (cx.float_type, parameters, "fminimumf") + } + sym::minimumf64 => { + let parameters = [ + cx.context.new_parameter(None, cx.double_type, "a"), + cx.context.new_parameter(None, cx.double_type, "b"), + ]; + (cx.double_type, parameters, "fminimum") + } + sym::minimumf128 => { + let f128_type = cx.type_f128(); + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html + let parameters = [ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ]; + (f128_type, parameters, "fminimumf128") + } + sym::maximumf32 => { + let parameters = [ + cx.context.new_parameter(None, cx.float_type, "a"), + cx.context.new_parameter(None, cx.float_type, "b"), + ]; + (cx.float_type, parameters, "fmaximumf") + } + sym::maximumf64 => { + let parameters = [ + cx.context.new_parameter(None, cx.double_type, "a"), + cx.context.new_parameter(None, cx.double_type, "b"), + ]; + (cx.double_type, parameters, "fmaximum") + } + sym::maximumf128 => { + let f128_type = cx.type_f128(); + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html + let parameters = [ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ]; + (f128_type, parameters, "fmaximumf128") + } + _ => return None, + }; + Some(cx.context.new_function( + None, + FunctionType::Extern, + return_type, + ¶meters, + func_name, + false, + )) +} + impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn codegen_intrinsic_call( &mut self, @@ -160,6 +224,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let simple = get_simple_intrinsic(self, name); + let simple_func = get_simple_function(self, name); // FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved: // https://github.com/rust-lang/rust-clippy/issues/12497 @@ -167,7 +232,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc #[allow(clippy::suspicious_else_formatting)] let value = match name { _ if simple.is_some() => { - let func = simple.expect("simple function"); + let func = simple.expect("simple intrinsic function"); + self.cx.context.new_call( + self.location, + func, + &args.iter().map(|arg| arg.immediate()).collect::>(), + ) + } + _ if simple_func.is_some() => { + let func = simple_func.expect("simple function"); self.cx.context.new_call( self.location, func, diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 2c5a78716838..6e2a50d745a4 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -16,7 +16,7 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(rustc_private, decl_macro, never_type, trusted_len, let_chains)] +#![feature(rustc_private, decl_macro, never_type, trusted_len)] #![allow(broken_intra_doc_links)] #![recursion_limit = "256"] #![warn(rust_2018_idioms)] @@ -454,7 +454,7 @@ impl WriteBackendMethods for GccCodegenBackend { } /// This is the entrypoint for a hot plugged rustc_codegen_gccjit -#[no_mangle] +#[unsafe(no_mangle)] pub fn __rustc_codegen_backend() -> Box { #[cfg(feature = "master")] let info = { diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 499c1a962311..0a01a661c357 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -10,7 +10,7 @@ tests/ui/sepcomp/sepcomp-fns-backwards.rs tests/ui/sepcomp/sepcomp-fns.rs tests/ui/sepcomp/sepcomp-statics.rs tests/ui/asm/x86_64/may_unwind.rs -tests/ui/catch-unwind-bang.rs +tests/ui/panics/catch-unwind-bang.rs tests/ui/drop/dynamic-drop-async.rs tests/ui/cfg/cfg-panic-abort.rs tests/ui/drop/repeat-drop.rs @@ -94,23 +94,14 @@ tests/ui/simd/intrinsic/generic-as.rs tests/ui/backtrace/backtrace.rs tests/ui/lifetimes/tail-expr-lock-poisoning.rs tests/ui/runtime/rt-explody-panic-payloads.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/function.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs tests/ui/codegen/equal-pointers-unequal/as-cast/segfault.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs index d5a0d71c4b29..bdcf14b4b26d 100644 --- a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs +++ b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs @@ -42,7 +42,9 @@ pub fn main_inner(profile: Profile) { .expect("failed to get absolute path of `gcc-path`") .display() .to_string(); - env::set_var("LD_LIBRARY_PATH", gcc_path); + unsafe { + env::set_var("LD_LIBRARY_PATH", gcc_path); + } fn rust_filter(path: &Path) -> bool { path.is_file() && path.extension().expect("extension").to_str().expect("to_str") == "rs" @@ -67,15 +69,14 @@ pub fn main_inner(profile: Profile) { .test_dir("tests/run") .test_path_filter(filter) .test_extract(|path| { - let lines = std::fs::read_to_string(path) + std::fs::read_to_string(path) .expect("read file") .lines() .skip_while(|l| !l.starts_with("//")) .take_while(|l| l.starts_with("//")) .map(|l| &l[2..]) .collect::>() - .join("\n"); - lines + .join("\n") }) .test_cmds(move |path| { // Test command 1: Compile `x.rs` into `tempdir/x`. diff --git a/compiler/rustc_codegen_gcc/tests/run/always_inline.rs b/compiler/rustc_codegen_gcc/tests/run/always_inline.rs new file mode 100644 index 000000000000..ebd741ee090c --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/always_inline.rs @@ -0,0 +1,53 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[inline(always)] +fn fib(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib(n - 1) + fib(n - 2) +} + +#[inline(always)] +fn fib_b(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib_a(n - 1) + fib_a(n - 2) +} + +#[inline(always)] +fn fib_a(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib_b(n - 1) + fib_b(n - 2) +} + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + if fib(2) != fib_a(2) { + intrinsics::abort(); + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/switchint_128bit.rs b/compiler/rustc_codegen_gcc/tests/run/switchint_128bit.rs new file mode 100644 index 000000000000..decae5bfcd78 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/switchint_128bit.rs @@ -0,0 +1,37 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use intrinsics::black_box; +use mini_core::*; + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + // 1st. Check that small 128 bit values work. + let val = black_box(64_u128); + match val { + 0 => return 1, + 1 => return 2, + 64 => (), + _ => return 3, + } + // 2nd check that *large* values work. + const BIG: u128 = 0xDEAD_C0FE_BEEF_DECAF_BADD_DECAF_BEEF_u128; + let val = black_box(BIG); + match val { + 0 => return 4, + 1 => return 5, + // Check that we will not match on the lower u64, if the upper qword is different! + 0xcafbadddecafbeef => return 6, + 0xDEAD_C0FE_BEEF_DECAF_BADD_DECAF_BEEF_u128 => (), + _ => return 7, + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py index 8efed3e43af8..181f1e501a40 100644 --- a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py +++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py @@ -12,7 +12,7 @@ def run_command(command, cwd=None): sys.exit(1) -def clone_repository(repo_name, path, repo_url, sub_paths=None): +def clone_repository(repo_name, path, repo_url, branch="master", sub_paths=None): if os.path.exists(path): while True: choice = input("There is already a `{}` folder, do you want to update it? [y/N]".format(path)) @@ -21,7 +21,7 @@ def clone_repository(repo_name, path, repo_url, sub_paths=None): return elif choice.lower() == "y": print("Updating repository...") - run_command(["git", "pull", "origin"], cwd=path) + run_command(["git", "pull", "origin", branch], cwd=path) return else: print("Didn't understand answer...") @@ -209,6 +209,7 @@ def main(): "llvm-project", llvm_path, "https://github.com/llvm/llvm-project", + branch="main", sub_paths=["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"], ) clone_repository( diff --git a/compiler/rustc_codegen_gcc/triagebot.toml b/compiler/rustc_codegen_gcc/triagebot.toml new file mode 100644 index 000000000000..13da0a87def3 --- /dev/null +++ b/compiler/rustc_codegen_gcc/triagebot.toml @@ -0,0 +1,7 @@ +# Documentation at https://forge.rust-lang.org/triagebot/index.html + +# Prevents un-canonicalized issue links (to avoid wrong issues being linked in r-l/rust) +[issue-links] + +# Prevents mentions in commits to avoid users being spammed +[no-mentions] diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index cbac55c71531..bf81eb648f8a 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -364,12 +364,7 @@ impl<'ll> CodegenCx<'ll, '_> { if !def_id.is_local() { let needs_dll_storage_attr = self.use_dll_storage_attrs - // If the symbol is a foreign item, then don't automatically apply DLLImport, as - // we'll rely on the #[link] attribute instead. BUT, if this is an internal symbol - // then it may be generated by the compiler in some crate, so we do need to apply - // DLLImport when linking with the MSVC linker. - && (!self.tcx.is_foreign_item(def_id) - || (self.sess().target.is_like_msvc && fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))) + && !self.tcx.is_foreign_item(def_id) // Local definitions can never be imported, so we must not apply // the DLLImport annotation. && !dso_local diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 97eebffd1fe8..d4c8ab80a331 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -58,5 +58,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 159c17b0af75..c5792da26781 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -768,7 +768,7 @@ fn link_natively( && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld") { info!("linker output: {:?}", out); - warn!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); + info!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); for arg in cmd.take_args() { if arg.to_string_lossy() != "-fuse-ld=lld" { cmd.arg(arg); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 80ee8ea22288..8fc83908efbc 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -337,12 +337,7 @@ pub(crate) trait Linker { fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ); + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]); fn subsystem(&mut self, subsystem: &str); fn linker_plugin_lto(&mut self); fn add_eh_frame_header(&mut self) {} @@ -775,12 +770,7 @@ impl<'a> Linker for GccLinker<'a> { } } - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility in object files typically takes care of this. if crate_type == CrateType::Executable { let should_export_executable_symbols = @@ -809,7 +799,7 @@ impl<'a> Linker for GccLinker<'a> { // Write a plain, newline-separated list of symbols let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { + for sym in symbols { debug!(" _{sym}"); writeln!(f, "_{sym}")?; } @@ -824,12 +814,11 @@ impl<'a> Linker for GccLinker<'a> { // .def file similar to MSVC one but without LIBRARY section // because LD doesn't like when it's empty writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; + for symbol in symbols { debug!(" _{symbol}"); // Quote the name in case it's reserved by linker in some way // (this accounts for names with dots in particular). - writeln!(f, " \"{symbol}\"{kind_marker}")?; + writeln!(f, " \"{symbol}\"")?; } }; if let Err(error) = res { @@ -842,7 +831,7 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "{{")?; if !symbols.is_empty() { writeln!(f, " global:")?; - for (sym, _) in symbols { + for sym in symbols { debug!(" {sym};"); writeln!(f, " {sym};")?; } @@ -1109,12 +1098,7 @@ impl<'a> Linker for MsvcLinker<'a> { // crates. Upstream rlibs may be linked statically to this dynamic library, // in which case they may continue to transitively be used and hence need // their symbols exported. - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility takes care of this typically if crate_type == CrateType::Executable { let should_export_executable_symbols = @@ -1132,10 +1116,9 @@ impl<'a> Linker for MsvcLinker<'a> { // straight to exports. writeln!(f, "LIBRARY")?; writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; + for symbol in symbols { debug!(" _{symbol}"); - writeln!(f, " {symbol}{kind_marker}")?; + writeln!(f, " {symbol}")?; } }; if let Err(error) = res { @@ -1276,19 +1259,14 @@ impl<'a> Linker for EmLinker<'a> { self.cc_arg("-nodefaultlibs"); } - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { debug!("EXPORTED SYMBOLS:"); self.cc_arg("-s"); let mut arg = OsString::from("EXPORTED_FUNCTIONS="); let encoded = serde_json::to_string( - &symbols.iter().map(|(sym, _)| "_".to_owned() + sym).collect::>(), + &symbols.iter().map(|sym| "_".to_owned() + sym).collect::>(), ) .unwrap(); debug!("{encoded}"); @@ -1450,13 +1428,8 @@ impl<'a> Linker for WasmLd<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { - for (sym, _) in symbols { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { + for sym in symbols { self.link_args(&["--export", sym]); } @@ -1590,7 +1563,7 @@ impl<'a> Linker for L4Bender<'a> { self.cc_arg("-nostdlib"); } - fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[(String, SymbolExportKind)]) { + fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) { // ToDo, not implemented, copy from GCC self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented); } @@ -1747,17 +1720,12 @@ impl<'a> Linker for AixLinker<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("list.exp"); let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. - for (symbol, _) in symbols { + for symbol in symbols { debug!(" _{symbol}"); writeln!(f, " {symbol}")?; } @@ -1801,12 +1769,9 @@ fn for_each_exported_symbols_include_dep<'tcx>( } } -pub(crate) fn exported_symbols( - tcx: TyCtxt<'_>, - crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { +pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { if let Some(ref exports) = tcx.sess.target.override_export_symbols { - return exports.iter().map(|name| (name.to_string(), SymbolExportKind::Text)).collect(); + return exports.iter().map(ToString::to_string).collect(); } if let CrateType::ProcMacro = crate_type { @@ -1816,10 +1781,7 @@ pub(crate) fn exported_symbols( } } -fn exported_symbols_for_non_proc_macro( - tcx: TyCtxt<'_>, - crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { +fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { let mut symbols = Vec::new(); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { @@ -1827,18 +1789,17 @@ fn exported_symbols_for_non_proc_macro( // from any cdylib. The latter doesn't work anyway as we use hidden visibility for // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning. if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) { - symbols.push(( - symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum), - info.kind, + symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate( + tcx, symbol, cnum, )); - symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, info, cnum); + symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum); } }); symbols } -fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, SymbolExportKind)> { +fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec { // `exported_symbols` will be empty when !should_codegen. if !tcx.sess.opts.output_types.should_codegen() { return Vec::new(); @@ -1848,10 +1809,7 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, Symbol let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx); - vec![ - (proc_macro_decls_name, SymbolExportKind::Text), - (metadata_symbol_name, SymbolExportKind::Text), - ] + vec![proc_macro_decls_name, metadata_symbol_name] } pub(crate) fn linked_symbols( @@ -1873,9 +1831,7 @@ pub(crate) fn linked_symbols( || info.used { symbols.push(( - symbol_export::linking_symbol_name_for_instance_in_crate( - tcx, symbol, info.kind, cnum, - ), + symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum), info.kind, )); } @@ -1950,13 +1906,7 @@ impl<'a> Linker for PtxLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - _symbols: &[(String, SymbolExportKind)], - ) { - } + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {} fn subsystem(&mut self, _subsystem: &str) {} @@ -2025,15 +1975,10 @@ impl<'a> Linker for LlbcLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { match _crate_type { CrateType::Cdylib => { - for (sym, _) in symbols { + for sym in symbols { self.link_args(&["--export-symbol", sym]); } } @@ -2107,16 +2052,11 @@ impl<'a> Linker for BpfLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("symbols"); let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { + for sym in symbols { writeln!(f, "{sym}")?; } }; diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 06ba5b4f6a75..5f0a0cf922af 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -692,7 +692,6 @@ fn calling_convention_for_symbol<'tcx>( pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>( tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, - export_kind: SymbolExportKind, instantiating_crate: CrateNum, ) -> String { let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate); @@ -713,9 +712,8 @@ pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>( let prefix = match &target.arch[..] { "x86" => Some('_'), "x86_64" => None, - // Only functions are decorated for arm64ec. - "arm64ec" if export_kind == SymbolExportKind::Text => Some('#'), - // Only x86/64 and arm64ec use symbol decorations. + "arm64ec" => Some('#'), + // Only x86/64 use symbol decorations. _ => return undecorated, }; @@ -755,10 +753,9 @@ pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>( /// Add it to the symbols list for all kernel functions, so that it is exported in the linked /// object. pub(crate) fn extend_exported_symbols<'tcx>( - symbols: &mut Vec<(String, SymbolExportKind)>, + symbols: &mut Vec, tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, - info: SymbolExportInfo, instantiating_crate: CrateNum, ) { let (conv, _) = calling_convention_for_symbol(tcx, symbol); @@ -770,7 +767,7 @@ pub(crate) fn extend_exported_symbols<'tcx>( let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate); // Add the symbol for the kernel descriptor (with .kd suffix) - symbols.push((format!("{undecorated}.kd"), info.kind)); + symbols.push(format!("{undecorated}.kd")); } fn maybe_emutls_symbol_name<'tcx>( diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 93cbd4cbb7cc..775ab9071e74 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -12,9 +12,9 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; +use rustc_hir::ItemId; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ItemId, Target}; use rustc_metadata::EncodedMetadata; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; @@ -1038,35 +1038,21 @@ impl CrateInfo { // by the compiler, but that's ok because all this stuff is unstable anyway. let target = &tcx.sess.target; if !are_upstream_rust_objects_already_included(tcx.sess) { - let add_prefix = match (target.is_like_windows, target.arch.as_ref()) { - (true, "x86") => |name: String, _: SymbolExportKind| format!("_{name}"), - (true, "arm64ec") => { - // Only functions are decorated for arm64ec. - |name: String, export_kind: SymbolExportKind| match export_kind { - SymbolExportKind::Text => format!("#{name}"), - _ => name, - } - } - _ => |name: String, _: SymbolExportKind| name, - }; - let missing_weak_lang_items: FxIndexSet<(Symbol, SymbolExportKind)> = info + let missing_weak_lang_items: FxIndexSet = info .used_crates .iter() .flat_map(|&cnum| tcx.missing_lang_items(cnum)) .filter(|l| l.is_weak()) .filter_map(|&l| { let name = l.link_name()?; - let export_kind = match l.target() { - Target::Fn => SymbolExportKind::Text, - Target::Static => SymbolExportKind::Data, - _ => bug!( - "Don't know what the export kind is for lang item of kind {:?}", - l.target() - ), - }; - lang_items::required(tcx, l).then_some((name, export_kind)) + lang_items::required(tcx, l).then_some(name) }) .collect(); + let prefix = match (target.is_like_windows, target.arch.as_ref()) { + (true, "x86") => "_", + (true, "arm64ec") => "#", + _ => "", + }; // This loop only adds new items to values of the hash map, so the order in which we // iterate over the values is not important. @@ -1079,13 +1065,10 @@ impl CrateInfo { .for_each(|(_, linked_symbols)| { let mut symbols = missing_weak_lang_items .iter() - .map(|(item, export_kind)| { + .map(|item| { ( - add_prefix( - mangle_internal_symbol(tcx, item.as_str()), - *export_kind, - ), - *export_kind, + format!("{prefix}{}", mangle_internal_symbol(tcx, item.as_str())), + SymbolExportKind::Text, ) }) .collect::>(); @@ -1100,12 +1083,12 @@ impl CrateInfo { // errors. linked_symbols.extend(ALLOCATOR_METHODS.iter().map(|method| { ( - add_prefix( + format!( + "{prefix}{}", mangle_internal_symbol( tcx, - global_fn_name(method.name).as_str(), - ), - SymbolExportKind::Text, + global_fn_name(method.name).as_str() + ) ), SymbolExportKind::Text, ) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 0e410be5a062..84919645cf07 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -218,7 +218,7 @@ pub struct CrateInfo { pub target_cpu: String, pub target_features: Vec, pub crate_types: Vec, - pub exported_symbols: UnordMap>, + pub exported_symbols: UnordMap>, pub linked_symbols: FxIndexMap>, pub local_crate_name: Symbol, pub compiler_builtins: Option, diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 216800717fda..405208e94f4b 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -353,8 +353,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if caller_fn_abi.conv != callee_fn_abi.conv { throw_ub_custom!( fluent::const_eval_incompatible_calling_conventions, - callee_conv = format!("{:?}", callee_fn_abi.conv), - caller_conv = format!("{:?}", caller_fn_abi.conv), + callee_conv = format!("{}", callee_fn_abi.conv), + caller_conv = format!("{}", caller_fn_abi.conv), ) } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index f48c73b13b96..f6a020116185 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -38,7 +38,7 @@ features = ["nightly"] # for may_dangle version = "0.12" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs index 4dbe11d707d7..9adae049261e 100644 --- a/compiler/rustc_data_structures/src/temp_dir.rs +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -17,7 +17,7 @@ impl Drop for MaybeTempDir { // occur. let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; if self.keep { - let _ = dir.into_path(); + let _ = dir.keep(); } } } diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 9da4f2dbc273..1971d06aad64 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -60,7 +60,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index b11793c190a1..82e7468211db 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -33,7 +33,7 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 809d1630ddec..820af9ac84b2 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -82,6 +82,8 @@ declare_features! ( (accepted, attr_literals, "1.30.0", Some(34981)), /// Allows overloading augmented assignment operations like `a += b`. (accepted, augmented_assignments, "1.8.0", Some(28235)), + /// Allows using `avx512*` target features. + (accepted, avx512_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), /// Allows mixing bind-by-move in patterns and references to those identifiers in guards. (accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287)), /// Allows bindings in the subpattern of a binding pattern. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8fb10736539a..6cdcf451f37e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -318,7 +318,6 @@ declare_features! ( (unstable, aarch64_ver_target_feature, "1.27.0", Some(44839)), (unstable, apx_target_feature, "1.88.0", Some(139284)), (unstable, arm_target_feature, "1.27.0", Some(44839)), - (unstable, avx512_target_feature, "1.27.0", Some(44839)), (unstable, bpf_target_feature, "1.54.0", Some(44839)), (unstable, csky_target_feature, "1.73.0", Some(44839)), (unstable, ermsb_target_feature, "1.49.0", Some(44839)), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 107aea4e5a40..fa1d1ec0a860 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2744,6 +2744,8 @@ pub enum ExprKind<'hir> { /// /// The "then" expr is always `ExprKind::Block`. If present, the "else" expr is always /// `ExprKind::Block` (for `else`) or `ExprKind::If` (for `else if`). + /// Note that using an `Expr` instead of a `Block` for the "then" part is intentional, + /// as it simplifies the type coercion machinery. If(&'hir Expr<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), /// A conditionless loop (can be exited with `break`, `continue`, or `return`). /// diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f92b2aea160a..da94331aa26f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1754,17 +1754,19 @@ pub(super) fn check_coroutine_obligations( debug!(?typeck_results.coroutine_stalled_predicates); let mode = if tcx.next_trait_solver_globally() { - TypingMode::post_borrowck_analysis(tcx, def_id) + // This query is conceptually between HIR typeck and + // MIR borrowck. We use the opaque types defined by HIR + // and ignore region constraints. + TypingMode::borrowck(tcx, def_id) } else { TypingMode::analysis_in_body(tcx, def_id) }; - let infcx = tcx - .infer_ctxt() - // typeck writeback gives us predicates with their regions erased. - // As borrowck already has checked lifetimes, we do not need to do it again. - .ignoring_regions() - .build(mode); + // Typeck writeback gives us predicates with their regions erased. + // We only need to check the goals while ignoring lifetimes to give good + // error message and to avoid breaking the assumption of `mir_borrowck` + // that all obligations already hold modulo regions. + let infcx = tcx.infer_ctxt().ignoring_regions().build(mode); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { @@ -1785,6 +1787,10 @@ pub(super) fn check_coroutine_obligations( let key = infcx.resolve_vars_if_possible(key); sanity_check_found_hidden_type(tcx, key, hidden_type)?; } + } else { + // We're not checking region constraints here, so we can simply drop the + // added opaque type uses in `TypingMode::Borrowck`. + let _ = infcx.take_opaque_types(); } Ok(()) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index fb67f2fd2235..2ee41e2efbd3 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use std::iter; use hir::def_id::{DefId, DefIdMap, LocalDefId}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir::def::{DefKind, Res}; @@ -356,61 +356,14 @@ fn compare_method_predicate_entailment<'tcx>( } if !(impl_sig, trait_sig).references_error() { - // Select obligations to make progress on inference before processing - // the wf obligation below. - // FIXME(-Znext-solver): Not needed when the hack below is removed. - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(errors); - return Err(reported); - } - - // See #108544. Annoying, we can end up in cases where, because of winnowing, - // we pick param env candidates over a more general impl, leading to more - // stricter lifetime requirements than we would otherwise need. This can - // trigger the lint. Instead, let's only consider type outlives and - // region outlives obligations. - // - // FIXME(-Znext-solver): Try removing this hack again once the new - // solver is stable. We should just be able to register a WF pred for - // the fn sig. - let mut wf_args: smallvec::SmallVec<[_; 4]> = - unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect(); - // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?)) - // will give back the well-formed predicate of the same array. - let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect(); - while let Some(term) = wf_args.pop() { - let Some(obligations) = rustc_trait_selection::traits::wf::obligations( - infcx, - param_env, - impl_m_def_id, - 0, - term, - impl_m_span, - ) else { - continue; - }; - for obligation in obligations { - debug!(?obligation); - match obligation.predicate.kind().skip_binder() { - // We need to register Projection oblgiations too, because we may end up with - // an implied `X::Item: 'a`, which gets desugared into `X::Item = ?0`, `?0: 'a`. - // If we only register the region outlives obligation, this leads to an unconstrained var. - // See `implied_bounds_entailment_alias_var.rs` test. - ty::PredicateKind::Clause( - ty::ClauseKind::RegionOutlives(..) - | ty::ClauseKind::TypeOutlives(..) - | ty::ClauseKind::Projection(..), - ) => ocx.register_obligation(obligation), - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { - if wf_args_seen.insert(term) { - wf_args.push(term) - } - } - _ => {} - } - } - } + ocx.register_obligation(traits::Obligation::new( + infcx.tcx, + cause, + param_env, + ty::ClauseKind::WellFormed( + Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig)).into(), + ), + )); } // Check that all obligations are satisfied by the implementation's diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 52656fc2d900..b92d1d7104f0 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -214,11 +214,9 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() let span = tcx.def_span(impl_did); let trait_name = "DispatchFromDyn"; - let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); - let source = trait_ref.self_ty(); let target = { - assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); + assert!(tcx.is_lang_item(trait_ref.def_id, LangItem::DispatchFromDyn)); trait_ref.args.type_at(1) }; @@ -339,7 +337,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() tcx, cause.clone(), param_env, - ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]), + ty::TraitRef::new(tcx, trait_ref.def_id, [ty_a, ty_b]), )); let errors = ocx.select_all_or_error(); if !errors.is_empty() { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 4937eb73a8be..010c6c376fe3 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -194,17 +194,6 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(()); }); - if tcx.features().rustc_attrs() { - tcx.sess.time("dumping_rustc_attr_data", || { - outlives::dump::inferred_outlives(tcx); - variance::dump::variances(tcx); - collect::dump::opaque_hidden_types(tcx); - collect::dump::predicates_and_item_bounds(tcx); - collect::dump::def_parents(tcx); - collect::dump::vtables(tcx); - }); - } - // Make sure we evaluate all static and (non-associated) const items, even if unused. // If any of these fail to evaluate, we do not want this crate to pass compilation. tcx.par_hir_body_owners(|item_def_id| { @@ -228,6 +217,17 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } }); + if tcx.features().rustc_attrs() { + tcx.sess.time("dumping_rustc_attr_data", || { + outlives::dump::inferred_outlives(tcx); + variance::dump::variances(tcx); + collect::dump::opaque_hidden_types(tcx); + collect::dump::predicates_and_item_bounds(tcx); + collect::dump::def_parents(tcx); + collect::dump::vtables(tcx); + }); + } + tcx.ensure_ok().check_unused_traits(()); } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 5bfc3e810d9f..c044c4f7c37d 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -490,11 +490,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { && let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From) { let ty = fcx.resolve_vars_if_possible(self.cast_ty); - // Erase regions to avoid panic in `prove_value` when calling - // `type_implements_trait`. - let ty = fcx.tcx.erase_regions(ty); let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let expr_ty = fcx.tcx.erase_regions(expr_ty); if fcx .infcx .type_implements_trait(from_trait, [ty, expr_ty], fcx.param_env) diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 8fd59999fce5..b1cb3ef4d796 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -1080,15 +1080,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that this is a projection from the `Future` trait. let trait_def_id = predicate.projection_term.trait_def_id(self.tcx); - let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); - if trait_def_id != future_trait { + if !self.tcx.is_lang_item(trait_def_id, LangItem::Future) { debug!("deduce_future_output_from_projection: not a future"); return None; } // The `Future` trait has only one associated item, `Output`, // so check that this is what we see. - let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0]; + let output_assoc_item = self.tcx.associated_item_def_ids(trait_def_id)[0]; if output_assoc_item != predicate.projection_term.def_id { span_bug!( cause_span, diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 732795535087..06103fe1c91b 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -680,6 +680,18 @@ pub(crate) enum SuggestBoxing { hir_typeck_suggest_boxing_when_appropriate, applicability = "machine-applicable" )] + ExprFieldShorthand { + #[suggestion_part(code = "{ident}: Box::new(")] + start: Span, + #[suggestion_part(code = ")")] + end: Span, + ident: Ident, + }, + #[note(hir_typeck_suggest_boxing_note)] + #[multipart_suggestion( + hir_typeck_suggest_boxing_when_appropriate, + applicability = "machine-applicable" + )] Other { #[suggestion_part(code = "Box::new(")] start: Span, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 91eb1989864f..1079262b5af8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -585,6 +585,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { errors::SuggestBoxing::AsyncBody } + _ if let Node::ExprField(expr_field) = self.tcx.parent_hir_node(hir_id) + && expr_field.is_shorthand => + { + errors::SuggestBoxing::ExprFieldShorthand { + start: span.shrink_to_lo(), + end: span.shrink_to_hi(), + ident: expr_field.ident, + } + } _ => errors::SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi(), @@ -2034,6 +2043,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { + Some(_) if expr.span.from_expansion() => return false, Some(ident) => format!(": {ident}{sugg}"), None => sugg.to_string(), }; diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index a8bbc89dbded..956671fc66ed 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -105,16 +105,26 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { } fn assign(&mut self, span: Span, nid: HirId, ty_opt: Option>) -> Ty<'tcx> { + // We evaluate expressions twice occasionally in diagnostics for better + // type information or because it needs type information out-of-order. + // In order to not ICE and not lead to knock-on ambiguity errors, if we + // try to re-assign a type to a local, then just take out the previous + // type and delay a bug. + if let Some(&local) = self.fcx.locals.borrow_mut().get(&nid) { + self.fcx.dcx().span_delayed_bug(span, "evaluated expression more than once"); + return local; + } + match ty_opt { None => { // Infer the variable's type. let var_ty = self.fcx.next_ty_var(span); - assert_eq!(self.fcx.locals.borrow_mut().insert(nid, var_ty), None); + self.fcx.locals.borrow_mut().insert(nid, var_ty); var_ty } Some(typ) => { // Take type that the user specified. - assert_eq!(self.fcx.locals.borrow_mut().insert(nid, typ), None); + self.fcx.locals.borrow_mut().insert(nid, typ); typ } } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 8dde99c45cfa..5fd98e35e5ce 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -98,6 +98,14 @@ impl<'tcx> InferCtxt<'tcx> { sub_region: Region<'tcx>, cause: &ObligationCause<'tcx>, ) { + // `is_global` means the type has no params, infer, placeholder, or non-`'static` + // free regions. If the type has none of these things, then we can skip registering + // this outlives obligation since it has no components which affect lifetime + // checking in an interesting way. + if sup_type.is_global() { + return; + } + debug!(?sup_type, ?sub_region, ?cause); let origin = SubregionOrigin::from_obligation_cause(cause, || { infer::RelateParamBound( diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7ed32d559c3a..e28639576f03 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -996,16 +996,11 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { sess.time("MIR_borrow_checking", || { tcx.par_hir_body_owners(|def_id| { - // Run unsafety check because it's responsible for stealing and - // deallocating THIR. - tcx.ensure_ok().check_unsafety(def_id); if !tcx.is_typeck_child(def_id.to_def_id()) { + // Child unsafety and borrowck happens together with the parent + tcx.ensure_ok().check_unsafety(def_id); tcx.ensure_ok().mir_borrowck(def_id) } - }); - }); - sess.time("MIR_effect_checking", || { - tcx.par_hir_body_owners(|def_id| { tcx.ensure_ok().has_ffi_unwind_calls(def_id); // If we need to codegen, ensure that we emit all errors from diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 99b42ee54802..08180bf8f8b2 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -362,6 +362,10 @@ lint_impl_trait_redundant_captures = all possible in-scope parameters are alread lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dereference of a raw pointer .note = creating a reference requires the pointer target to be valid and imposes aliasing requirements + .raw_ptr = this raw pointer has type `{$raw_ptr_ty}` + .autoref = autoref is being applied to this expression, resulting in: `{$autoref_ty}` + .overloaded_deref = references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations + .method_def = method calls to `{$method_name}` require a reference .suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 91d58d92466a..5de2cbf9939d 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -4,7 +4,10 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDer use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; -use crate::lints::{ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsSuggestion}; +use crate::lints::{ + ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsMethodNote, ImplicitUnsafeAutorefsOrigin, + ImplicitUnsafeAutorefsSuggestion, +}; use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { @@ -92,25 +95,37 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { && let adjustments = peel_derefs_adjustments(&**adjustments) // 3. An automatically inserted reference (might come from a deref). && let [adjustment] = adjustments - && let Some(borrow_mutbl) = has_implicit_borrow(adjustment) + && let Some((borrow_mutbl, through_overloaded_deref)) = has_implicit_borrow(adjustment) && let ExprKind::Unary(UnOp::Deref, dereferenced) = // 2. Any number of place projections. peel_place_mappers(inner).kind // 1. Deref of a raw pointer. && typeck.expr_ty(dereferenced).is_raw_ptr() - // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]` - && match expr.kind { - ExprKind::MethodCall(..) => matches!( - cx.typeck_results().type_dependent_def_id(expr.hir_id), - Some(def_id) if cx.tcx.has_attr(def_id, sym::rustc_no_implicit_autorefs) - ), - _ => true, + && let method_did = match expr.kind { + // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]` + ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + _ => None, } + && method_did.map(|did| cx.tcx.has_attr(did, sym::rustc_no_implicit_autorefs)).unwrap_or(true) { cx.emit_span_lint( DANGEROUS_IMPLICIT_AUTOREFS, expr.span.source_callsite(), ImplicitUnsafeAutorefsDiag { + raw_ptr_span: dereferenced.span, + raw_ptr_ty: typeck.expr_ty(dereferenced), + origin: if through_overloaded_deref { + ImplicitUnsafeAutorefsOrigin::OverloadedDeref + } else { + ImplicitUnsafeAutorefsOrigin::Autoref { + autoref_span: inner.span, + autoref_ty: typeck.expr_ty_adjusted(inner), + } + }, + method: method_did.map(|did| ImplicitUnsafeAutorefsMethodNote { + def_span: cx.tcx.def_span(did), + method_name: cx.tcx.item_name(did), + }), suggestion: ImplicitUnsafeAutorefsSuggestion { mutbl: borrow_mutbl.ref_prefix_str(), deref: if is_coming_from_deref { "*" } else { "" }, @@ -146,11 +161,12 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen /// Test if some adjustment has some implicit borrow. /// -/// Returns `Some(mutability)` if the argument adjustment has implicit borrow in it. -fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option { +/// Returns `Some((mutability, was_an_overloaded_deref))` if the argument adjustment is +/// an implicit borrow (or has an implicit borrow via an overloaded deref). +fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { match kind { - &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some(mutbl), - &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some(mutbl.into()), + &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), + &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::ReborrowPin(..) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 487184b836a4..7268a7f704fc 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -59,11 +59,38 @@ pub(crate) enum ShadowedIntoIterDiagSub { #[derive(LintDiagnostic)] #[diag(lint_implicit_unsafe_autorefs)] #[note] -pub(crate) struct ImplicitUnsafeAutorefsDiag { +pub(crate) struct ImplicitUnsafeAutorefsDiag<'a> { + #[label(lint_raw_ptr)] + pub raw_ptr_span: Span, + pub raw_ptr_ty: Ty<'a>, + #[subdiagnostic] + pub origin: ImplicitUnsafeAutorefsOrigin<'a>, + #[subdiagnostic] + pub method: Option, #[subdiagnostic] pub suggestion: ImplicitUnsafeAutorefsSuggestion, } +#[derive(Subdiagnostic)] +pub(crate) enum ImplicitUnsafeAutorefsOrigin<'a> { + #[note(lint_autoref)] + Autoref { + #[primary_span] + autoref_span: Span, + autoref_ty: Ty<'a>, + }, + #[note(lint_overloaded_deref)] + OverloadedDeref, +} + +#[derive(Subdiagnostic)] +#[note(lint_method_def)] +pub(crate) struct ImplicitUnsafeAutorefsMethodNote { + #[primary_span] + pub def_span: Span, + pub method_name: Symbol, +} + #[derive(Subdiagnostic)] #[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] pub(crate) struct ImplicitUnsafeAutorefsSuggestion { diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 6692ea735401..a662694ac38f 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -255,6 +255,7 @@ fn main() { } else if target.contains("haiku") || target.contains("darwin") || (is_crossed && (target.contains("dragonfly") || target.contains("solaris"))) + || target.contains("cygwin") { println!("cargo:rustc-link-lib=z"); } else if target.contains("netbsd") { diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 33fb13e23bf8..ee377277017a 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -267,6 +267,18 @@ fn add_query_desc_cached_impl( ) { let Query { name, key, modifiers, .. } = &query; + // This dead code exists to instruct rust-analyzer about the link between the `rustc_queries` + // query names and the corresponding produced provider. The issue is that by nature of this + // macro producing a higher order macro that has all its token in the macro declaration we lose + // any meaningful spans, resulting in rust-analyzer being unable to make the connection between + // the query name and the corresponding providers field. The trick to fix this is to have + // `rustc_queries` emit a field access with the given name's span which allows it to succesfully + // show references / go to definition to the correspondig provider assignment which is usually + // the more interesting place. + let ra_hint = quote! { + let crate::query::Providers { #name: _, .. }; + }; + // Find out if we should cache the query on disk let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); @@ -277,6 +289,7 @@ fn add_query_desc_cached_impl( #[allow(unused_variables, unused_braces, rustc::pass_by_value)] #[inline] pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::query::queries::#name::Key<'tcx>) -> bool { + #ra_hint #expr } } @@ -286,6 +299,7 @@ fn add_query_desc_cached_impl( #[allow(rustc::pass_by_value)] #[inline] pub fn #name<'tcx>(_: TyCtxt<'tcx>, _: &crate::query::queries::#name::Key<'tcx>) -> bool { + #ra_hint false } } diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 64a1f2aff15c..1d67d0fe3bbf 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -22,7 +22,7 @@ impl SymbolExportLevel { } /// Kind of exported symbols. -#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable, Hash)] +#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable)] pub enum SymbolExportKind { Text, Data, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dda0faa3afed..b2a58897c31b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -52,6 +52,7 @@ pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym}; pub use rustc_type_ir::data_structures::{DelayedMap, DelayedSet}; +pub use rustc_type_ir::fast_reject::DeepRejectCtxt; #[allow( hidden_glob_reexports, rustc::usage_of_type_ir_inherent, @@ -120,6 +121,7 @@ use crate::ty; use crate::ty::codec::{TyDecoder, TyEncoder}; pub use crate::ty::diagnostics::*; use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::LayoutError; use crate::ty::util::Discr; use crate::ty::walk::TypeWalker; @@ -1184,7 +1186,7 @@ pub struct Destructor { #[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)] pub struct AsyncDestructor { /// The `DefId` of the `impl AsyncDrop` - pub impl_did: LocalDefId, + pub impl_did: DefId, } #[derive(Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] @@ -1877,6 +1879,11 @@ impl<'tcx> TyCtxt<'tcx> { self.def_kind(trait_def_id) == DefKind::TraitAlias } + /// Arena-alloc of LayoutError for coroutine layout + fn layout_error(self, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> { + self.arena.alloc(err) + } + /// Returns layout of a non-async-drop coroutine. Layout might be unavailable if the /// coroutine is tainted by errors. /// @@ -1885,12 +1892,14 @@ impl<'tcx> TyCtxt<'tcx> { fn ordinary_coroutine_layout( self, def_id: DefId, - coroutine_kind_ty: Ty<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + args: GenericArgsRef<'tcx>, + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let coroutine_kind_ty = args.as_coroutine().kind_ty(); let mir = self.optimized_mir(def_id); + let ty = || Ty::new_coroutine(self, def_id, args); // Regular coroutine if coroutine_kind_ty.is_unit() { - mir.coroutine_layout_raw() + mir.coroutine_layout_raw().ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { // If we have a `Coroutine` that comes from an coroutine-closure, // then it may be a by-move or by-ref body. @@ -1904,6 +1913,7 @@ impl<'tcx> TyCtxt<'tcx> { // a by-ref coroutine. if identity_kind_ty == coroutine_kind_ty { mir.coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { assert_matches!(coroutine_kind_ty.to_opt_closure_kind(), Some(ClosureKind::FnOnce)); assert_matches!( @@ -1912,6 +1922,7 @@ impl<'tcx> TyCtxt<'tcx> { ); self.optimized_mir(self.coroutine_by_move_body_def_id(def_id)) .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } } } @@ -1923,12 +1934,15 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let ty = || Ty::new_coroutine(self, def_id, args); if args[0].has_placeholders() || args[0].has_non_region_param() { - return None; + return Err(self.layout_error(LayoutError::TooGeneric(ty()))); } let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args)); - self.mir_shims(instance).coroutine_layout_raw() + self.mir_shims(instance) + .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } /// Returns layout of a coroutine. Layout might be unavailable if the @@ -1937,7 +1951,7 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { if self.is_async_drop_in_place_coroutine(def_id) { // layout of `async_drop_in_place::{closure}` in case, // when T is a coroutine, contains this internal coroutine's ptr in upvars @@ -1959,12 +1973,12 @@ impl<'tcx> TyCtxt<'tcx> { variant_source_info, storage_conflicts: BitMatrix::new(0, 0), }; - return Some(self.arena.alloc(proxy_layout)); + return Ok(self.arena.alloc(proxy_layout)); } else { self.async_drop_coroutine_layout(def_id, args) } } else { - self.ordinary_coroutine_layout(def_id, args.as_coroutine().kind_ty()) + self.ordinary_coroutine_layout(def_id, args) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0250c777faf7..6fd6aff0e2b2 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1453,9 +1453,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // contain named regions. So we erase and anonymize everything // here to compare the types modulo regions below. let proj = cx.tcx().erase_regions(proj); - let proj = cx.tcx().anonymize_bound_vars(proj); let super_proj = cx.tcx().erase_regions(super_proj); - let super_proj = cx.tcx().anonymize_bound_vars(super_proj); proj == super_proj }); diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c31ce1bc6307..ab1f3d6099fc 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1260,8 +1260,7 @@ impl<'tcx> Ty<'tcx> { return true; }; alloc.expect_ty().ty_adt_def().is_some_and(|alloc_adt| { - let global_alloc = tcx.require_lang_item(LangItem::GlobalAlloc, None); - alloc_adt.did() == global_alloc + tcx.is_lang_item(alloc_adt.did(), LangItem::GlobalAlloc) }) } _ => false, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 6fe5927c29fc..9676aa404482 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -465,7 +465,7 @@ impl<'tcx> TyCtxt<'tcx> { dtor_candidate = Some(impl_did); } - Some(ty::AsyncDestructor { impl_did: dtor_candidate? }) + Some(ty::AsyncDestructor { impl_did: dtor_candidate?.into() }) } /// Returns the set of types that are required to be alive in diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index adfce99a9b53..9d0681b19b9d 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -1148,8 +1148,9 @@ impl UnsafeOpKind { pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Closures and inline consts are handled by their owner, if it has a body + assert!(!tcx.is_typeck_child(def.to_def_id())); // Also, don't safety check custom MIR - if tcx.is_typeck_child(def.to_def_id()) || tcx.has_attr(def, sym::custom_mir) { + if tcx.has_attr(def, sym::custom_mir) { return; } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index b5cd41334598..92ee80eaa353 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -32,7 +32,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { let mut rval_ty = rvalue.ty(self.local_decls, self.tcx); // Not erasing this causes `Free Regions` errors in validator, // when rval is `ReStatic`. - rval_ty = self.tcx.erase_regions_ty(rval_ty); + rval_ty = self.tcx.erase_regions(rval_ty); place_ty = self.tcx.erase_regions(place_ty); if place_ty != rval_ty { let temp = self diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8b8d1efbbd2e..209e818e9e32 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -836,6 +836,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn simplify_rvalue( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -855,7 +856,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Repeat(op, amount) } Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty), - Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location), + Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location), Rvalue::Ref(_, borrow_kind, ref mut place) => { self.simplify_place_projection(place, location); return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind))); @@ -943,6 +944,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate_to_copy( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, fields: &[VnIndex], @@ -982,12 +984,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Allow introducing places with non-constant offsets, as those are still better than // reconstructing an aggregate. - if let Some(place) = self.try_as_place(copy_from_local_value, location, true) { - if rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty { + if let Some(place) = self.try_as_place(copy_from_local_value, location, true) + && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty + { + // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments. + // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections. + if lhs.as_local().is_some() { self.reused_locals.insert(place.local); *rvalue = Rvalue::Use(Operand::Copy(place)); - return Some(copy_from_local_value); } + return Some(copy_from_local_value); } None @@ -995,6 +1001,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -1090,7 +1097,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let AggregateTy::Def(_, _) = ty && let Some(value) = - self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index) + self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) { return Some(value); } @@ -1765,7 +1772,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind { self.simplify_place_projection(lhs, location); - let value = self.simplify_rvalue(rvalue, location); + let value = self.simplify_rvalue(lhs, rvalue, location); let value = if let Some(local) = lhs.as_local() && self.ssa.is_ssa(local) // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index f541a32cd264..f8d1629b0e26 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -752,7 +752,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let layout = if def_id == self.caller_body.source.def_id() { self.caller_body .coroutine_layout_raw() - .or_else(|| self.tcx.coroutine_layout(def_id, args)) + .or_else(|| self.tcx.coroutine_layout(def_id, args).ok()) } else if self.tcx.needs_coroutine_by_move_body_def_id(def_id) && let ty::ClosureKind::FnOnce = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() @@ -762,7 +762,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // Same if this is the by-move body of a coroutine-closure. self.caller_body.coroutine_layout_raw() } else { - self.tcx.coroutine_layout(def_id, args) + self.tcx.coroutine_layout(def_id, args).ok() }; let Some(layout) = layout else { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 345a272895d3..542e212e1bfb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,6 +2,7 @@ pub(super) mod structural_traits; +use std::cell::Cell; use std::ops::ControlFlow; use derive_where::derive_where; @@ -117,24 +118,24 @@ where ) -> Result, NoSolution> { Self::fast_reject_assumption(ecx, goal, assumption)?; - ecx.probe(|candidate: &Result, NoSolution>| match candidate { - Ok(candidate) => inspect::ProbeKind::TraitCandidate { - source: candidate.source, - result: Ok(candidate.result), - }, - Err(NoSolution) => inspect::ProbeKind::TraitCandidate { - source: CandidateSource::ParamEnv(ParamEnvSource::Global), - result: Err(NoSolution), - }, + // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily + // check whether the candidate is global while considering normalization. + // + // We need to write into `source` inside of `match_assumption`, but need to access it + // in `probe` even if the candidate does not apply before we get there. We handle this + // by using a `Cell` here. We only ever write into it inside of `match_assumption`. + let source = Cell::new(CandidateSource::ParamEnv(ParamEnvSource::Global)); + ecx.probe(|result: &QueryResult| inspect::ProbeKind::TraitCandidate { + source: source.get(), + result: *result, }) .enter(|ecx| { - Self::match_assumption(ecx, goal, assumption)?; - let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?; - Ok(Candidate { - source, - result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?, + Self::match_assumption(ecx, goal, assumption, |ecx| { + source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) }) + .map(|result| Candidate { source: source.get(), result }) } /// Try equating an assumption predicate against a goal's predicate. If it @@ -150,10 +151,8 @@ where ) -> Result, NoSolution> { Self::fast_reject_assumption(ecx, goal, assumption)?; - ecx.probe_trait_candidate(source).enter(|ecx| { - Self::match_assumption(ecx, goal, assumption)?; - then(ecx) - }) + ecx.probe_trait_candidate(source) + .enter(|ecx| Self::match_assumption(ecx, goal, assumption, then)) } /// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`] @@ -169,7 +168,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution>; + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult; fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 84a83d79cf04..8413c2abbb96 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -61,13 +61,14 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { let host_clause = assumption.as_host_effect_clause().unwrap(); let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; - Ok(()) + then(ecx) } /// Register additional assumptions for aliases corresponding to `~const` item bounds. diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index b90e34e78101..2fddc0044cb9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -129,7 +129,40 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { + let cx = ecx.cx(); + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if the GAT arguments are fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + match goal.predicate.alias.kind(cx) { + ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { + for arg in goal.predicate.alias.own_args(cx).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + let term = ecx.structurally_normalize_term(goal.param_env, term)?; + if term.is_infer() { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + } + } + ty::AliasTermKind::OpaqueTy + | ty::AliasTermKind::InherentTy + | ty::AliasTermKind::InherentConst + | ty::AliasTermKind::FreeTy + | ty::AliasTermKind::FreeConst + | ty::AliasTermKind::UnevaluatedConst => {} + } + let projection_pred = assumption.as_projection_clause().unwrap(); let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); @@ -139,7 +172,6 @@ where // Add GAT where clauses from the trait's definition // FIXME: We don't need these, since these are the type's own WF obligations. - let cx = ecx.cx(); ecx.add_goals( GoalSource::AliasWellFormed, cx.own_predicates_of(goal.predicate.def_id()) @@ -147,7 +179,7 @@ where .map(|pred| goal.with(cx, pred)), ); - Ok(()) + then(ecx) } fn consider_additional_alias_assumptions( diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index e3addf8bf93f..966d5422fbb7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - NoSolution, ParamEnvSource, + NoSolution, ParamEnvSource, QueryResult, }; impl assembly::GoalKind for TraitPredicate @@ -150,13 +150,14 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { let trait_clause = assumption.as_trait_clause().unwrap(); let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; - Ok(()) + then(ecx) } fn consider_auto_trait_candidate( diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f88c15785d33..a6919afef12c 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -815,7 +815,6 @@ parse_switch_ref_box_order = switch the order of `ref` and `box` .suggestion = swap them parse_ternary_operator = Rust has no ternary operator - .help = use an `if-else` expression instead parse_tilde_is_not_unary_operator = `~` cannot be used as a unary operator .suggestion = use `!` to perform bitwise not @@ -963,6 +962,8 @@ parse_use_empty_block_not_semi = expected { "`{}`" }, found `;` parse_use_eq_instead = unexpected `==` .suggestion = try using `=` instead +parse_use_if_else = use an `if-else` expression instead + parse_use_let_not_auto = write `let` instead of `auto` to introduce a new variable parse_use_let_not_var = write `let` instead of `var` to introduce a new variable diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 766baf6f80c7..31a48b22cfee 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -436,10 +436,28 @@ pub(crate) enum IfExpressionMissingThenBlockSub { #[derive(Diagnostic)] #[diag(parse_ternary_operator)] -#[help] pub(crate) struct TernaryOperator { #[primary_span] pub span: Span, + /// If we have a span for the condition expression, suggest the if/else + #[subdiagnostic] + pub sugg: Option, + /// Otherwise, just print the suggestion message + #[help(parse_use_if_else)] + pub no_sugg: bool, +} + +#[derive(Subdiagnostic, Copy, Clone)] +#[multipart_suggestion(parse_use_if_else, applicability = "maybe-incorrect", style = "verbose")] +pub(crate) struct TernaryOperatorSuggestion { + #[suggestion_part(code = "if ")] + pub before_cond: Span, + #[suggestion_part(code = "{{")] + pub question: Span, + #[suggestion_part(code = "}} else {{")] + pub colon: Span, + #[suggestion_part(code = " }}")] + pub end: Span, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 23c8db7bca78..6277dde7c974 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -41,8 +41,9 @@ use crate::errors::{ IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, - TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::parser::attr::InnerAttrPolicy; use crate::{exp, fluent_generated as fluent}; @@ -497,7 +498,7 @@ impl<'a> Parser<'a> { // If the user is trying to write a ternary expression, recover it and // return an Err to prevent a cascade of irrelevant diagnostics. if self.prev_token == token::Question - && let Err(e) = self.maybe_recover_from_ternary_operator() + && let Err(e) = self.maybe_recover_from_ternary_operator(None) { return Err(e); } @@ -1602,12 +1603,18 @@ impl<'a> Parser<'a> { /// Rust has no ternary operator (`cond ? then : else`). Parse it and try /// to recover from it if `then` and `else` are valid expressions. Returns /// an err if this appears to be a ternary expression. - pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> { + /// If we have the span of the condition, we can provide a better error span + /// and code suggestion. + pub(super) fn maybe_recover_from_ternary_operator( + &mut self, + cond: Option, + ) -> PResult<'a, ()> { if self.prev_token != token::Question { return PResult::Ok(()); } - let lo = self.prev_token.span.lo(); + let question = self.prev_token.span; + let lo = cond.unwrap_or(question).lo(); let snapshot = self.create_snapshot_for_diagnostic(); if match self.parse_expr() { @@ -1620,11 +1627,20 @@ impl<'a> Parser<'a> { } } { if self.eat_noexpect(&token::Colon) { + let colon = self.prev_token.span; match self.parse_expr() { - Ok(_) => { - return Err(self - .dcx() - .create_err(TernaryOperator { span: self.token.span.with_lo(lo) })); + Ok(expr) => { + let sugg = cond.map(|cond| TernaryOperatorSuggestion { + before_cond: cond.shrink_to_lo(), + question, + colon, + end: expr.span.shrink_to_hi(), + }); + return Err(self.dcx().create_err(TernaryOperator { + span: self.prev_token.span.with_lo(lo), + sugg, + no_sugg: sugg.is_none(), + })); } Err(err) => { err.cancel(); diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 885a65d4de7a..396ded96bde1 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -879,7 +879,12 @@ impl<'a> Parser<'a> { { // Just check for errors and recover; do not eat semicolon yet. - let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]); + let expect_result = + if let Err(e) = self.maybe_recover_from_ternary_operator(Some(expr.span)) { + Err(e) + } else { + self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]) + }; // Try to both emit a better diagnostic, and avoid further errors by replacing // the `expr` with `ExprKind::Err`. diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index f63d8b2d79f6..2b85d7b26cea 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -6,7 +6,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![allow(unused_crate_dependencies)] -#![cfg_attr(all(feature = "rustc", bootstrap), feature(let_chains))] // tidy-alphabetical-end pub mod constructor; diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 0b16983c2c79..d09750fa281b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2493,7 +2493,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else { return None; }; - let module_name = crate_module.kind.name().unwrap_or(kw::Empty); + let module_name = crate_module.kind.name().unwrap_or(kw::Crate); let import_snippet = match import.kind { ImportKind::Single { source, target, .. } if source != target => { format!("{source} as {target}") diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index faee0e7dd5ff..1b682d0cf8ae 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -111,6 +111,19 @@ enum PatBoundCtx { Or, } +/// Tracks bindings resolved within a pattern. This serves two purposes: +/// +/// - This tracks when identifiers are bound multiple times within a pattern. In a product context, +/// this is an error. In an or-pattern, this lets us reuse the same resolution for each instance. +/// See `fresh_binding` and `resolve_pattern_inner` for more information. +/// +/// - The guard expression of a guard pattern may use bindings from within the guard pattern, but +/// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the +/// subpattern to construct the scope for the guard. +/// +/// Each identifier must map to at most one distinct [`Res`]. +type PatternBindings = SmallVec<[(PatBoundCtx, FxIndexMap); 1]>; + /// Does this the item (from the item rib scope) allow generic parameters? #[derive(Copy, Clone, Debug)] pub(crate) enum HasGenericParams { @@ -786,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r fn visit_pat(&mut self, p: &'ast Pat) { let prev = self.diag_metadata.current_pat; self.diag_metadata.current_pat = Some(p); - visit::walk_pat(self, p); + + if let PatKind::Guard(subpat, _) = &p.kind { + // We walk the guard expression in `resolve_pattern_inner`. Don't resolve it twice. + self.visit_pat(subpat); + } else { + visit::walk_pat(self, p); + } + self.diag_metadata.current_pat = prev; } fn visit_local(&mut self, local: &'ast Local) { @@ -2297,7 +2317,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_fn_params( &mut self, has_self: bool, - inputs: impl Iterator, &'ast Ty)>, + inputs: impl Iterator, &'ast Ty)> + Clone, ) -> Result, Vec)> { enum Elision { /// We have not found any candidate. @@ -2319,15 +2339,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let mut parameter_info = Vec::new(); let mut all_candidates = Vec::new(); + // Resolve and apply bindings first so diagnostics can see if they're used in types. let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - for (index, (pat, ty)) in inputs.enumerate() { - debug!(?pat, ?ty); + for (pat, _) in inputs.clone() { + debug!("resolving bindings in pat = {pat:?}"); self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { if let Some(pat) = pat { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } }); + } + self.apply_pattern_bindings(bindings); + for (index, (pat, ty)) in inputs.enumerate() { + debug!("resolving type for pat = {pat:?}, ty = {ty:?}"); // Record elision candidates only for this parameter. debug_assert_matches!(self.lifetime_elision_candidates, None); self.lifetime_elision_candidates = Some(Default::default()); @@ -3615,16 +3640,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.visit_path(&delegation.path, delegation.id); let Some(body) = &delegation.body else { return }; self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - let span = delegation.path.segments.last().unwrap().ident.span; - this.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); + let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules()); + let res = Res::Local(delegation.id); + this.innermost_rib_bindings(ValueNS).insert(ident, res); this.visit_block(body); }); } @@ -3635,6 +3654,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { for Param { pat, .. } in params { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } + this.apply_pattern_bindings(bindings); }); for Param { ty, .. } in params { self.visit_ty(ty); @@ -3851,13 +3871,32 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; self.resolve_pattern(pat, pat_src, &mut bindings); + self.apply_pattern_bindings(bindings); } + /// Apply the bindings from a pattern to the innermost rib of the current scope. + fn apply_pattern_bindings(&mut self, mut pat_bindings: PatternBindings) { + let rib_bindings = self.innermost_rib_bindings(ValueNS); + let Some((_, pat_bindings)) = pat_bindings.pop() else { + bug!("tried applying nonexistent bindings from pattern"); + }; + + if rib_bindings.is_empty() { + // Often, such as for match arms, the bindings are introduced into a new rib. + // In this case, we can move the bindings over directly. + *rib_bindings = pat_bindings; + } else { + rib_bindings.extend(pat_bindings); + } + } + + /// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce + /// the bindings into scope. fn resolve_pattern( &mut self, pat: &'ast Pat, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) { // We walk the pattern before declaring the pattern's inner bindings, // so that we avoid resolving a literal expression to a binding defined @@ -3890,9 +3929,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { #[tracing::instrument(skip(self, bindings), level = "debug")] fn resolve_pattern_inner( &mut self, - pat: &Pat, + pat: &'ast Pat, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) { // Visit all direct subpatterns of this pattern. pat.walk(&mut |pat| { @@ -3950,6 +3989,31 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // Prevent visiting `ps` as we've already done so above. return false; } + PatKind::Guard(ref subpat, ref guard) => { + // Add a new set of bindings to the stack to collect bindings in `subpat`. + bindings.push((PatBoundCtx::Product, Default::default())); + // Resolving `subpat` adds bindings onto the newly-pushed context. After, the + // total number of contexts on the stack should be the same as before. + let binding_ctx_stack_len = bindings.len(); + self.resolve_pattern_inner(subpat, pat_src, bindings); + assert_eq!(bindings.len(), binding_ctx_stack_len); + // These bindings, but none from the surrounding pattern, are visible in the + // guard; put them in scope and resolve `guard`. + let subpat_bindings = bindings.pop().unwrap().1; + self.with_rib(ValueNS, RibKind::Normal, |this| { + *this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone(); + this.resolve_expr(guard, None); + }); + // Propagate the subpattern's bindings upwards. + // FIXME(guard_patterns): For `if let` guards, we'll also need to get the + // bindings introduced by the guard from its rib and propagate them upwards. + // This will require checking the identifiers for overlaps with `bindings`, like + // what `fresh_binding` does (ideally sharing its logic). To keep them separate + // from `subpat_bindings`, we can introduce a fresh rib for the guard. + bindings.last_mut().unwrap().1.extend(subpat_bindings); + // Prevent visiting `subpat` as we've already done so above. + return false; + } _ => {} } true @@ -3988,20 +4052,17 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ident: Ident, pat_id: NodeId, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) -> Res { - // Add the binding to the local ribs, if it doesn't already exist in the bindings map. + // Add the binding to the bindings map, if it doesn't already exist. // (We must not add it if it's in the bindings map because that breaks the assumptions // later passes make about or-patterns.) let ident = ident.normalize_to_macro_rules(); - let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident)); // Already bound in a product pattern? e.g. `(a, a)` which is not allowed. - let already_bound_and = bound_iter.clone().any(|(ctx, _)| *ctx == PatBoundCtx::Product); - // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`. - // This is *required* for consistency which is checked later. - let already_bound_or = bound_iter.any(|(ctx, _)| *ctx == PatBoundCtx::Or); - + let already_bound_and = bindings + .iter() + .any(|(ctx, map)| *ctx == PatBoundCtx::Product && map.contains_key(&ident)); if already_bound_and { // Overlap in a product pattern somewhere; report an error. use ResolutionError::*; @@ -4014,19 +4075,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.report_error(ident.span, error(ident)); } - // Record as bound. - bindings.last_mut().unwrap().1.insert(ident); - - if already_bound_or { + // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`. + // This is *required* for consistency which is checked later. + let already_bound_or = bindings + .iter() + .find_map(|(ctx, map)| if *ctx == PatBoundCtx::Or { map.get(&ident) } else { None }); + let res = if let Some(&res) = already_bound_or { // `Variant1(a) | Variant2(a)`, ok // Reuse definition from the first `a`. - self.innermost_rib_bindings(ValueNS)[&ident] - } else { - // A completely fresh binding is added to the set. - let res = Res::Local(pat_id); - self.innermost_rib_bindings(ValueNS).insert(ident, res); res - } + } else { + // A completely fresh binding is added to the map. + Res::Local(pat_id) + }; + + // Record as bound. + bindings.last_mut().unwrap().1.insert(ident, res); + res } fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut FxIndexMap { diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 63772a322221..f0ee19e3c677 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -33,7 +33,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index bdeca91eb64a..207ba5157bdb 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -78,10 +78,16 @@ fn current_dll_path() -> Result { if libc::dladdr(addr, &mut info) == 0 { return Err("dladdr failed".into()); } - if info.dli_fname.is_null() { - return Err("dladdr returned null pointer".into()); - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = { + if info.dli_fname.is_null() { + return Err("dladdr returned null pointer".into()); + } + info.dli_fname + }; + let bytes = CStr::from_ptr(fname_ptr).to_bytes(); let os = OsStr::from_bytes(bytes); Ok(PathBuf::from(os)) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3a95447308a9..efae6250b072 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -280,6 +280,8 @@ symbols! { IoSeek, IoWrite, IpAddr, + Ipv4Addr, + Ipv6Addr, IrTyKind, Is, Item, diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index ae366e29e323..907614520a2c 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::str::FromStr; use std::{fmt, iter}; @@ -895,6 +896,37 @@ impl FromStr for Conv { } } +fn conv_to_externabi(conv: &Conv) -> ExternAbi { + match conv { + Conv::C => ExternAbi::C { unwind: false }, + Conv::Rust => ExternAbi::Rust, + Conv::PreserveMost => ExternAbi::RustCold, + Conv::ArmAapcs => ExternAbi::Aapcs { unwind: false }, + Conv::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, + Conv::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry, + Conv::Msp430Intr => ExternAbi::Msp430Interrupt, + Conv::GpuKernel => ExternAbi::GpuKernel, + Conv::X86Fastcall => ExternAbi::Fastcall { unwind: false }, + Conv::X86Intr => ExternAbi::X86Interrupt, + Conv::X86Stdcall => ExternAbi::Stdcall { unwind: false }, + Conv::X86ThisCall => ExternAbi::Thiscall { unwind: false }, + Conv::X86VectorCall => ExternAbi::Vectorcall { unwind: false }, + Conv::X86_64SysV => ExternAbi::SysV64 { unwind: false }, + Conv::X86_64Win64 => ExternAbi::Win64 { unwind: false }, + Conv::AvrInterrupt => ExternAbi::AvrInterrupt, + Conv::AvrNonBlockingInterrupt => ExternAbi::AvrNonBlockingInterrupt, + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => ExternAbi::RiscvInterruptM, + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor } => ExternAbi::RiscvInterruptS, + Conv::Cold | Conv::PreserveAll => unreachable!(), + } +} + +impl Display for Conv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", conv_to_externabi(self)) + } +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_target/src/spec/base/msvc.rs b/compiler/rustc_target/src/spec/base/msvc.rs index bd59678d2366..486d7158723f 100644 --- a/compiler/rustc_target/src/spec/base/msvc.rs +++ b/compiler/rustc_target/src/spec/base/msvc.rs @@ -5,19 +5,7 @@ use crate::spec::{BinaryFormat, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo pub(crate) fn opts() -> TargetOptions { // Suppress the verbose logo and authorship debugging output, which would needlessly // clog any log files. - let pre_link_args = TargetOptions::link_args( - LinkerFlavor::Msvc(Lld::No), - &[ - "/NOLOGO", - // "Symbol is marked as dllimport, but defined in an object file" - // Harmless warning that flags a potential performance improvement: marking a symbol as - // dllimport indirects usage via the `__imp_` symbol, which isn't required if the symbol - // is in the current binary. This is tripped by __rust_no_alloc_shim_is_unstable as it - // is generated by the compiler, but marked as a foreign item (hence the dllimport) in - // the standard library. - "/IGNORE:4286", - ], - ); + let pre_link_args = TargetOptions::link_args(LinkerFlavor::Msvc(Lld::No), &["/NOLOGO"]); TargetOptions { linker_flavor: LinkerFlavor::Msvc(Lld::No), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs index c5704c574483..f1b6fa123deb 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs @@ -1,4 +1,4 @@ -use crate::spec::{FramePointer, LinkerFlavor, Lld, Target, TargetMetadata, base}; +use crate::spec::{FramePointer, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); @@ -11,11 +11,6 @@ pub(crate) fn target() -> Target { // and other services. It must point to the previous {x29, x30} pair on the stack." base.frame_pointer = FramePointer::NonLeaf; - // MSVC emits a warning about code that may trip "Cortex-A53 MPCore processor bug #843419" (see - // https://developer.arm.com/documentation/epm048406/latest) which is sometimes emitted by LLVM. - // Since Arm64 Windows 10+ isn't supported on that processor, it's safe to disable the warning. - base.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &["/arm64hazardfree"]); - Target { llvm_target: "aarch64-pc-windows-msvc".into(), metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5428aa4cf708..99b04ac27200 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -416,25 +416,25 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ), ("avx10.2", Unstable(sym::avx10_target_feature), &["avx10.1"]), ("avx2", Stable, &["avx"]), - ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avx512bf16", Stable, &["avx512bw"]), + ("avx512bitalg", Stable, &["avx512bw"]), + ("avx512bw", Stable, &["avx512f"]), + ("avx512cd", Stable, &["avx512f"]), + ("avx512dq", Stable, &["avx512f"]), + ("avx512f", Stable, &["avx2", "fma", "f16c"]), + ("avx512fp16", Stable, &["avx512bw"]), + ("avx512ifma", Stable, &["avx512f"]), + ("avx512vbmi", Stable, &["avx512bw"]), + ("avx512vbmi2", Stable, &["avx512bw"]), + ("avx512vl", Stable, &["avx512f"]), + ("avx512vnni", Stable, &["avx512f"]), + ("avx512vp2intersect", Stable, &["avx512f"]), + ("avx512vpopcntdq", Stable, &["avx512f"]), + ("avxifma", Stable, &["avx2"]), + ("avxneconvert", Stable, &["avx2"]), + ("avxvnni", Stable, &["avx2"]), + ("avxvnniint16", Stable, &["avx2"]), + ("avxvnniint8", Stable, &["avx2"]), ("bmi1", Stable, &[]), ("bmi2", Stable, &[]), ("cmpxchg16b", Stable, &[]), @@ -442,7 +442,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f16c", Stable, &["avx"]), ("fma", Stable, &["avx"]), ("fxsr", Stable, &[]), - ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("gfni", Stable, &["sse2"]), ("kl", Unstable(sym::keylocker_x86), &["sse2"]), ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), ("lzcnt", Stable, &[]), @@ -469,8 +469,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), ("ssse3", Stable, &["sse3"]), ("tbm", Unstable(sym::tbm_target_feature), &[]), - ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("vaes", Stable, &["avx2", "aes"]), + ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]), ("widekl", Unstable(sym::keylocker_x86), &["kl"]), ("x87", Unstable(sym::x87_target_feature), &[]), ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 5b938456e03b..31b075db04b9 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -690,24 +690,26 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( /// used during analysis. pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec>) -> bool { debug!("impossible_predicates(predicates={:?})", predicates); - let (infcx, param_env) = - tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized()); + let (infcx, param_env) = tcx + .infer_ctxt() + .with_next_trait_solver(true) + .build_with_typing_env(ty::TypingEnv::fully_monomorphized()); + let ocx = ObligationCtxt::new(&infcx); let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates); for predicate in predicates { let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate); ocx.register_obligation(obligation); } - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - return true; - } - - // Leak check for any higher-ranked trait mismatches. - // We only need to do this in the old solver, since the new solver already - // leak-checks. - if !infcx.next_trait_solver() && infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() { + // Use `select_where_possible` to only return impossible for true errors, + // and not ambiguities or overflows. Since the new trait solver forces + // some currently undetected overlap between `dyn Trait: Trait` built-in + // vs user-written impls to AMBIGUOUS, this may return ambiguity even + // with no infer vars. There may also be ways to encounter ambiguity due + // to post-mono overflow. + let true_errors = ocx.select_where_possible(); + if !true_errors.is_empty() { return true; } diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index f04a5feba301..38cfdcdc22d3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -196,11 +196,31 @@ where debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); ty } else { - ocx.deeply_normalize(&cause, param_env, ty)?; + // Flush errors b/c `deeply_normalize` doesn't expect pending + // obligations, and we may have pending obligations from the + // branch above (from other types). + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + return Err(errors); + } - let errors = ocx.select_where_possible(); - debug!("normalize errors: {ty} ~> {errors:#?}"); - return Err(errors); + // When query normalization fails, we don't get back an interesting + // reason that we could use to report an error in borrowck. In order to turn + // this into a reportable error, we deeply normalize again. We don't expect + // this to succeed, so delay a bug if it does. + match ocx.deeply_normalize(&cause, param_env, ty) { + Ok(_) => { + tcx.dcx().span_delayed_bug( + span, + format!( + "query normalize succeeded of {ty}, \ + but deep normalize failed", + ), + ); + ty + } + Err(errors) => return Err(errors), + } }; match ty.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4ce37db42800..44a76f6e0832 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, - TypingMode, Upcast, elaborate, + self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, TypingMode, Upcast, elaborate, }; use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; @@ -1241,7 +1241,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { self.infcx.tcx.trait_is_coinductive(data.def_id()) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => true, + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { + // FIXME(generic_const_exprs): GCE needs well-formedness predicates to be + // coinductive, but GCE is on the way out anyways, so this should eventually + // be replaced with `false`. + self.infcx.tcx.features().generic_const_exprs() + } _ => false, }) } @@ -1669,6 +1674,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(()); } + let drcx = DeepRejectCtxt::relate_rigid_rigid(self.infcx.tcx); + let obligation_args = obligation.predicate.skip_binder().trait_ref.args; + if !drcx.args_may_unify(obligation_args, trait_bound.skip_binder().args) { + return Err(()); + } + let trait_bound = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, HigherRankedType, @@ -1760,12 +1771,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if is_match { let generics = self.tcx().generics_of(obligation.predicate.def_id); - // FIXME(generic-associated-types): Addresses aggressive inference in #92917. + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. // If this type is a GAT, and of the GAT args resolve to something new, // that means that we must have newly inferred something about the GAT. // We should give up in that case. - // FIXME(generic-associated-types): This only detects one layer of inference, - // which is probably not what we actually want, but fixing it causes some ambiguity: + // + // This only detects one layer of inference, which is probably not what we actually + // want, but fixing it causes some ambiguity: // . if !generics.is_own_empty() && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 908fcb14cb2f..ad57555bd24d 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -492,9 +492,7 @@ fn layout_of_uncached<'tcx>( ty::Coroutine(def_id, args) => { use rustc_middle::ty::layout::PrimitiveExt as _; - let Some(info) = tcx.coroutine_layout(def_id, args) else { - return Err(error(cx, LayoutError::Unknown(ty))); - }; + let info = tcx.coroutine_layout(def_id, args)?; let local_layouts = info .field_tys diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 7423a156a217..c929de116241 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -8,15 +8,6 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); - // Type-level uninhabitedness should always imply ABI uninhabitedness. - if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) { - assert!( - layout.is_uninhabited(), - "{:?} is type-level uninhabited but not ABI-uninhabited?", - layout.ty - ); - } - if layout.size.bytes() % layout.align.abi.bytes() != 0 { bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); } @@ -29,6 +20,19 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou return; } + // Type-level uninhabitedness should always imply ABI uninhabitedness. This can be expensive on + // big non-exhaustive types, and is [hard to + // fix](https://github.com/rust-lang/rust/issues/141006#issuecomment-2883415000) in general. + // Only doing this sanity check when debug assertions are turned on avoids the issue for the + // very specific case of #140944. + if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) { + assert!( + layout.is_uninhabited(), + "{:?} is type-level uninhabited but not ABI-uninhabited?", + layout.ty + ); + } + /// Yields non-ZST fields of the type fn non_zst_fields<'tcx, 'a>( cx: &'a LayoutCx<'tcx>, diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 11ec1f0a9fb0..34502f495500 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -232,6 +232,9 @@ impl bool { + if lhs == rhs { + return true; + } self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH) } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index e6e6466766be..ee4a8096462a 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -298,6 +298,14 @@ pub trait GenericArg>: + From + From { + fn as_term(&self) -> Option { + match self.kind() { + ty::GenericArgKind::Lifetime(_) => None, + ty::GenericArgKind::Type(ty) => Some(ty.into()), + ty::GenericArgKind::Const(ct) => Some(ct.into()), + } + } + fn as_type(&self) -> Option { if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None } } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index b59495b93c83..f02d9c988c8d 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -682,6 +682,13 @@ impl AliasTerm { pub fn trait_ref(self, interner: I) -> TraitRef { self.trait_ref_and_own_args(interner).0 } + + /// Extract the own args from this projection. + /// For example, if this is a projection of `::Item<'a>`, + /// then this function would return the slice `['a]` as the own args. + pub fn own_args(self, interner: I) -> I::GenericArgsSlice { + self.trait_ref_and_own_args(interner).1 + } } /// The following methods work only with inherent associated term projections. diff --git a/library/Cargo.lock b/library/Cargo.lock index 97ca3cb06b24..02018057ed53 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -196,7 +196,6 @@ name = "panic_abort" version = "0.0.0" dependencies = [ "alloc", - "cfg-if", "compiler_builtins", "core", "libc", diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index a989e5b55b3d..3e006a2d1bdf 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -287,7 +287,7 @@ impl RawVec { } #[inline] - pub(crate) fn non_null(&self) -> NonNull { + pub(crate) const fn non_null(&self) -> NonNull { self.inner.non_null() } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 4e42a5da7eac..37614a7ca457 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2826,7 +2826,54 @@ impl SpecToString for bool { } } +macro_rules! impl_to_string { + ($($signed:ident, $unsigned:ident,)*) => { + $( + #[cfg(not(no_global_oom_handling))] + #[cfg(not(feature = "optimize_for_size"))] + impl SpecToString for $signed { + #[inline] + fn spec_to_string(&self) -> String { + const SIZE: usize = $signed::MAX.ilog(10) as usize + 1; + let mut buf = [core::mem::MaybeUninit::::uninit(); SIZE]; + // Only difference between signed and unsigned are these 8 lines. + let mut out; + if *self < 0 { + out = String::with_capacity(SIZE + 1); + out.push('-'); + } else { + out = String::with_capacity(SIZE); + } + + out.push_str(self.unsigned_abs()._fmt(&mut buf)); + out + } + } + #[cfg(not(no_global_oom_handling))] + #[cfg(not(feature = "optimize_for_size"))] + impl SpecToString for $unsigned { + #[inline] + fn spec_to_string(&self) -> String { + const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1; + let mut buf = [core::mem::MaybeUninit::::uninit(); SIZE]; + + self._fmt(&mut buf).to_string() + } + } + )* + } +} + +impl_to_string! { + i8, u8, + i16, u16, + i32, u32, + i64, u64, + isize, usize, +} + #[cfg(not(no_global_oom_handling))] +#[cfg(feature = "optimize_for_size")] impl SpecToString for u8 { #[inline] fn spec_to_string(&self) -> String { @@ -2846,6 +2893,7 @@ impl SpecToString for u8 { } #[cfg(not(no_global_oom_handling))] +#[cfg(feature = "optimize_for_size")] impl SpecToString for i8 { #[inline] fn spec_to_string(&self) -> String { diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index a97912304c89..59879f23d785 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1816,10 +1816,10 @@ impl Vec { /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[rustc_const_unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] #[inline] - pub fn as_non_null(&mut self) -> NonNull { - // SAFETY: A `Vec` always has a non-null pointer. - unsafe { NonNull::new_unchecked(self.as_mut_ptr()) } + pub const fn as_non_null(&mut self) -> NonNull { + self.buf.non_null() } /// Returns a reference to the underlying allocator. diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 99e52d0ada0a..83ba17b93f51 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -35,4 +35,10 @@ check-cfg = [ # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 'cfg(feature, values(any()))', + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', ] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index efa7bed7c8e1..4476e3f79238 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -531,6 +531,7 @@ impl [T; N] { /// let y = x.map(|v| v.len()); /// assert_eq!(y, [6, 9, 3, 3]); /// ``` + #[must_use] #[stable(feature = "array_map", since = "1.55.0")] pub fn map(self, f: F) -> [U; N] where diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 042925a352f3..af2edf141b22 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -4,6 +4,7 @@ use super::*; use crate::panic::const_panic; use crate::slice; use crate::str::from_utf8_unchecked_mut; +use crate::ub_checks::assert_unsafe_precondition; use crate::unicode::printable::is_printable; use crate::unicode::{self, conversions}; @@ -1202,6 +1203,26 @@ impl char { } } + /// Converts this char into an [ASCII character](`ascii::Char`), without + /// checking whether it is valid. + /// + /// # Safety + /// + /// This char must be within the ASCII range, or else this is UB. + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the char is valid ASCII", + (it: &char = self) => it.is_ascii() + ); + + // SAFETY: the caller promised that this char is ASCII. + unsafe { ascii::Char::from_u8_unchecked(*self as u8) } + } + /// Makes a copy of the value in its ASCII upper case equivalent. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index e1b10e1074d2..c542a28beb87 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -464,8 +464,8 @@ pub trait Into: Sized { /// orphaning rules. /// See [`Into`] for more details. /// -/// Prefer using [`Into`] over using `From` when specifying trait bounds on a generic function. -/// This way, types that directly implement [`Into`] can be used as arguments as well. +/// Prefer using [`Into`] over [`From`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`Into`] can be used as well. /// /// The `From` trait is also very useful when performing error handling. When constructing a function /// that is capable of failing, the return type will generally be of the form `Result`. @@ -597,6 +597,9 @@ pub trait From: Sized { /// standard library. For more information on this, see the /// documentation for [`Into`]. /// +/// Prefer using [`TryInto`] over [`TryFrom`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`TryInto`] can be used as well. +/// /// # Implementing `TryInto` /// /// This suffers the same restrictions and reasoning as implementing @@ -636,6 +639,9 @@ pub trait TryInto: Sized { /// When the [`!`] type is stabilized [`Infallible`] and [`!`] will be /// equivalent. /// +/// Prefer using [`TryInto`] over [`TryFrom`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`TryInto`] can be used as well. +/// /// `TryFrom` can be implemented as follows: /// /// ``` diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 870ad9df4fd3..556db239f249 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -20,6 +20,8 @@ macro_rules! impl_general_format { } } +#[cfg(target_has_reliable_f16)] +impl_general_format! { f16 } impl_general_format! { f32 f64 } // Don't inline this so callers don't use the stack space this function @@ -231,6 +233,13 @@ macro_rules! floating { floating! { f32 f64 } +#[cfg(target_has_reliable_f16)] +floating! { f16 } + +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f16 { #[inline] @@ -239,6 +248,33 @@ impl Debug for f16 { } } +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl LowerExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl UpperExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f128 { #[inline] diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 4467b37bd451..ba30518d70bc 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,7 +208,11 @@ macro_rules! impl_Display { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "optimize_for_size"))] { - self._fmt(true, f) + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + + f.pad_integral(true, "", self._fmt(&mut buf)) } #[cfg(feature = "optimize_for_size")] { @@ -222,7 +226,11 @@ macro_rules! impl_Display { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "optimize_for_size"))] { - return self.unsigned_abs()._fmt(*self >= 0, f); + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + + f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf)) } #[cfg(feature = "optimize_for_size")] { @@ -233,10 +241,13 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $unsigned { - fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; - // Buffer decimals for $unsigned with right alignment. - let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + #[doc(hidden)] + #[unstable( + feature = "fmt_internals", + reason = "specialized method meant to only be used by `SpecToString` implementation", + issue = "none" + )] + pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::]) -> &'a str { // Count the number of bytes in buf that are not initialized. let mut offset = buf.len(); // Consume the least-significant decimals from a working copy. @@ -301,13 +312,12 @@ macro_rules! impl_Display { // SAFETY: All buf content since offset is set. let written = unsafe { buf.get_unchecked(offset..) }; // SAFETY: Writes use ASCII from the lookup table exclusively. - let as_str = unsafe { + unsafe { str::from_utf8_unchecked(slice::from_raw_parts( MaybeUninit::slice_as_ptr(written), written.len(), )) - }; - f.pad_integral(is_nonnegative, "", as_str) + } } })* diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 64a7ec8906b6..e605d7e0d784 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -101,6 +101,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cfg_match)] +#![feature(cfg_target_has_reliable_f16_f128)] #![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] @@ -111,7 +112,6 @@ #![feature(is_ascii_octdigit)] #![feature(lazy_get)] #![feature(link_cfg)] -#![feature(non_null_from_ref)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(ptr_alignment_type)] @@ -188,9 +188,9 @@ // // Target features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(avx512_target_feature))] #![feature(aarch64_unstable_target_feature)] #![feature(arm_target_feature)] -#![feature(avx512_target_feature)] #![feature(hexagon_target_feature)] #![feature(keylocker_x86)] #![feature(loongarch_target_feature)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index d0be82adb6b1..63a479ed8dd4 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -391,7 +391,7 @@ impl MaybeUninit { /// For your convenience, this also returns a mutable reference to the /// (now safely initialized) contents of `self`. /// - /// As the content is stored inside a `MaybeUninit`, the destructor is not + /// As the content is stored inside a `ManuallyDrop`, the destructor is not /// run for the inner data if the MaybeUninit leaves scope without a call to /// [`assume_init`], [`assume_init_drop`], or similar. Code that receives /// the mutable reference returned by this function needs to keep this in diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 2f027be69286..aaa68e8d7d1a 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -68,6 +68,7 @@ pub enum IpAddr { /// assert!("0000000.0.0.0".parse::().is_err()); // first octet is a zero in octal /// assert!("0xcb.0x0.0x71.0x00".parse::().is_err()); // all octets are in hex /// ``` +#[rustc_diagnostic_item = "Ipv4Addr"] #[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv4Addr { @@ -160,6 +161,7 @@ impl Hash for Ipv4Addr { /// assert_eq!("::1".parse(), Ok(localhost)); /// assert_eq!(localhost.is_loopback(), true); /// ``` +#[rustc_diagnostic_item = "Ipv6Addr"] #[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv6Addr { diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs index b8a28a675691..5bf0faf0bc91 100644 --- a/library/core/src/num/dec2flt/float.rs +++ b/library/core/src/num/dec2flt/float.rs @@ -45,7 +45,7 @@ macro_rules! int { } } -int!(u32, u64); +int!(u16, u32, u64); /// A helper trait to avoid duplicating basically all the conversion code for IEEE floats. /// @@ -189,9 +189,14 @@ pub trait RawFloat: /// Returns the mantissa, exponent and sign as integers. /// - /// That is, this returns `(m, p, s)` such that `s * m * 2^p` represents the original float. - /// For 0, the exponent will be `-(EXP_BIAS + SIG_BITS`, which is the - /// minimum subnormal power. + /// This returns `(m, p, s)` such that `s * m * 2^p` represents the original float. For 0, the + /// exponent will be `-(EXP_BIAS + SIG_BITS)`, which is the minimum subnormal power. For + /// infinity or NaN, the exponent will be `EXP_SAT - EXP_BIAS - SIG_BITS`. + /// + /// If subnormal, the mantissa will be shifted one bit to the left. Otherwise, it is returned + /// with the explicit bit set but otherwise unshifted + /// + /// `s` is only ever +/-1. fn integer_decode(self) -> (u64, i16, i8) { let bits = self.to_bits(); let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 }; @@ -213,6 +218,50 @@ const fn pow2_to_pow10(a: i64) -> i64 { res as i64 } +#[cfg(target_has_reliable_f16)] +impl RawFloat for f16 { + type Int = u16; + + const INFINITY: Self = Self::INFINITY; + const NEG_INFINITY: Self = Self::NEG_INFINITY; + const NAN: Self = Self::NAN; + const NEG_NAN: Self = -Self::NAN; + + const BITS: u32 = 16; + const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; + const EXP_MASK: Self::Int = Self::EXP_MASK; + const SIG_MASK: Self::Int = Self::MAN_MASK; + + const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -22; + const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5; + const SMALLEST_POWER_OF_TEN: i32 = -27; + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } + + #[inline] + fn from_u64_bits(v: u64) -> Self { + Self::from_bits((v & 0xFFFF) as u16) + } + + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; + TABLE[exponent & 7] + } + + fn to_bits(self) -> Self::Int { + self.to_bits() + } + + fn classify(self) -> FpCategory { + self.classify() + } +} + impl RawFloat for f32 { type Int = u32; diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index d1a0e1db3131..abad7acb1046 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -171,9 +171,25 @@ macro_rules! from_str_float_impl { } }; } + +#[cfg(target_has_reliable_f16)] +from_str_float_impl!(f16); from_str_float_impl!(f32); from_str_float_impl!(f64); +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] +impl FromStr for f16 { + type Err = ParseFloatError; + + #[inline] + fn from_str(_src: &str) -> Result { + unimplemented!("requires target_has_reliable_f16") + } +} + /// An error which can be returned when parsing a float. /// /// This error is used as the error type for the [`FromStr`] implementation diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 7e470185c86d..0c2c4155d66c 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1415,3 +1415,413 @@ impl f128 { intrinsics::frem_algebraic(self, rhs) } } + +// Functions in this module fall into `core_float_math` +// FIXME(f16_f128): all doctests must be gated to platforms that have `long double` === `_Float128` +// due to https://github.com/llvm/llvm-project/issues/44744. aarch64 linux matches this. +// #[unstable(feature = "core_float_math", issue = "137578")] +#[cfg(not(test))] +impl f128 { + /// Returns the largest integer less than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f128 { + intrinsics::round_ties_even_f128(self) + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f128 { + self - self.trunc() + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f128, b: f128) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[doc(alias = "squareRoot")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf128(self) } + } +} diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e47900cba550..1a859f2277ff 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -13,6 +13,8 @@ use crate::convert::FloatToInt; use crate::num::FpCategory; +#[cfg(not(test))] +use crate::num::libm; use crate::panic::const_assert; use crate::{intrinsics, mem}; @@ -1391,3 +1393,446 @@ impl f16 { intrinsics::frem_algebraic(self, rhs) } } + +// Functions in this module fall into `core_float_math` +// #[unstable(feature = "core_float_math", issue = "137578")] +#[cfg(not(test))] +impl f16 { + /// Returns the largest integer less than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f16 { + intrinsics::round_ties_even_f16(self) + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f16 { + self - self.trunc() + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f16, b: f16) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[doc(alias = "squareRoot")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f16 { + libm::cbrtf(self as f32) as f16 + } +} diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 5fbc6eb33f17..9525bdb6762a 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -12,7 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -use crate::num::FpCategory; +use crate::num::{FpCategory, libm}; use crate::panic::const_assert; use crate::{cfg_match, intrinsics, mem}; @@ -1556,3 +1556,414 @@ impl f32 { intrinsics::frem_algebraic(self, rhs) } } + +/// Experimental version of `floor` in `core`. See [`f32::floor`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.7_f32; +/// let g = 3.0_f32; +/// let h = -3.7_f32; +/// +/// assert_eq!(f32::floor(f), 3.0); +/// assert_eq!(f32::floor(g), 3.0); +/// assert_eq!(f32::floor(h), -4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::floor`]: ../../std/primitive.f32.html#method.floor +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn floor(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf32(x) } +} + +/// Experimental version of `ceil` in `core`. See [`f32::ceil`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.01_f32; +/// let g = 4.0_f32; +/// +/// assert_eq!(f32::ceil(f), 4.0); +/// assert_eq!(f32::ceil(g), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::ceil`]: ../../std/primitive.f32.html#method.ceil +#[inline] +#[doc(alias = "ceiling")] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn ceil(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf32(x) } +} + +/// Experimental version of `round` in `core`. See [`f32::round`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.3_f32; +/// let g = -3.3_f32; +/// let h = -3.7_f32; +/// let i = 3.5_f32; +/// let j = 4.5_f32; +/// +/// assert_eq!(f32::round(f), 3.0); +/// assert_eq!(f32::round(g), -3.0); +/// assert_eq!(f32::round(h), -4.0); +/// assert_eq!(f32::round(i), 4.0); +/// assert_eq!(f32::round(j), 5.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::round`]: ../../std/primitive.f32.html#method.round +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf32(x) } +} + +/// Experimental version of `round_ties_even` in `core`. See [`f32::round_ties_even`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.3_f32; +/// let g = -3.3_f32; +/// let h = 3.5_f32; +/// let i = 4.5_f32; +/// +/// assert_eq!(f32::round_ties_even(f), 3.0); +/// assert_eq!(f32::round_ties_even(g), -3.0); +/// assert_eq!(f32::round_ties_even(h), 4.0); +/// assert_eq!(f32::round_ties_even(i), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::round_ties_even`]: ../../std/primitive.f32.html#method.round_ties_even +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round_ties_even(x: f32) -> f32 { + intrinsics::round_ties_even_f32(x) +} + +/// Experimental version of `trunc` in `core`. See [`f32::trunc`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.7_f32; +/// let g = 3.0_f32; +/// let h = -3.7_f32; +/// +/// assert_eq!(f32::trunc(f), 3.0); +/// assert_eq!(f32::trunc(g), 3.0); +/// assert_eq!(f32::trunc(h), -3.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::trunc`]: ../../std/primitive.f32.html#method.trunc +#[inline] +#[doc(alias = "truncate")] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn trunc(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf32(x) } +} + +/// Experimental version of `fract` in `core`. See [`f32::fract`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 3.6_f32; +/// let y = -3.6_f32; +/// let abs_difference_x = (f32::fract(x) - 0.6).abs(); +/// let abs_difference_y = (f32::fract(y) - (-0.6)).abs(); +/// +/// assert!(abs_difference_x <= f32::EPSILON); +/// assert!(abs_difference_y <= f32::EPSILON); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::fract`]: ../../std/primitive.f32.html#method.fract +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn fract(x: f32) -> f32 { + x - trunc(x) +} + +/// Experimental version of `mul_add` in `core`. See [`f32::mul_add`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// # // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +/// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { +/// use core::f32; +/// +/// let m = 10.0_f32; +/// let x = 4.0_f32; +/// let b = 60.0_f32; +/// +/// assert_eq!(f32::mul_add(m, x, b), 100.0); +/// assert_eq!(m * x + b, 100.0); +/// +/// let one_plus_eps = 1.0_f32 + f32::EPSILON; +/// let one_minus_eps = 1.0_f32 - f32::EPSILON; +/// let minus_one = -1.0_f32; +/// +/// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. +/// assert_eq!(f32::mul_add(one_plus_eps, one_minus_eps, minus_one), -f32::EPSILON * f32::EPSILON); +/// // Different rounding with the non-fused multiply and add. +/// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); +/// # } +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::mul_add`]: ../../std/primitive.f32.html#method.mul_add +#[inline] +#[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn mul_add(x: f32, y: f32, z: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf32(x, y, z) } +} + +/// Experimental version of `div_euclid` in `core`. See [`f32::div_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let a: f32 = 7.0; +/// let b = 4.0; +/// assert_eq!(f32::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 +/// assert_eq!(f32::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 +/// assert_eq!(f32::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 +/// assert_eq!(f32::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::div_euclid`]: ../../std/primitive.f32.html#method.div_euclid +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn div_euclid(x: f32, rhs: f32) -> f32 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q +} + +/// Experimental version of `rem_euclid` in `core`. See [`f32::rem_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let a: f32 = 7.0; +/// let b = 4.0; +/// assert_eq!(f32::rem_euclid(a, b), 3.0); +/// assert_eq!(f32::rem_euclid(-a, b), 1.0); +/// assert_eq!(f32::rem_euclid(a, -b), 3.0); +/// assert_eq!(f32::rem_euclid(-a, -b), 1.0); +/// // limitation due to round-off error +/// assert!(f32::rem_euclid(-f32::EPSILON, 3.0) != 0.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::rem_euclid`]: ../../std/primitive.f32.html#method.rem_euclid +#[inline] +#[doc(alias = "modulo", alias = "mod")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn rem_euclid(x: f32, rhs: f32) -> f32 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } +} + +/// Experimental version of `powi` in `core`. See [`f32::powi`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 2.0_f32; +/// let abs_difference = (f32::powi(x, 2) - (x * x)).abs(); +/// assert!(abs_difference <= f32::EPSILON); +/// +/// assert_eq!(f32::powi(f32::NAN, 0), 1.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::powi`]: ../../std/primitive.f32.html#method.powi +#[inline] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn powi(x: f32, n: i32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif32(x, n) } +} + +/// Experimental version of `sqrt` in `core`. See [`f32::sqrt`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let positive = 4.0_f32; +/// let negative = -4.0_f32; +/// let negative_zero = -0.0_f32; +/// +/// assert_eq!(f32::sqrt(positive), 2.0); +/// assert!(f32::sqrt(negative).is_nan()); +/// assert_eq!(f32::sqrt(negative_zero), negative_zero); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::sqrt`]: ../../std/primitive.f32.html#method.sqrt +#[inline] +#[doc(alias = "squareRoot")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn sqrt(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf32(x) } +} + +/// Experimental version of `abs_sub` in `core`. See [`f32::abs_sub`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 3.0f32; +/// let y = -3.0f32; +/// +/// let abs_difference_x = (f32::abs_sub(x, 1.0) - 2.0).abs(); +/// let abs_difference_y = (f32::abs_sub(y, 1.0) - 0.0).abs(); +/// +/// assert!(abs_difference_x <= f32::EPSILON); +/// assert!(abs_difference_y <= f32::EPSILON); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::abs_sub`]: ../../std/primitive.f32.html#method.abs_sub +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[deprecated( + since = "1.10.0", + note = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ + known as `fdimf` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdimf`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too)." +)] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn abs_sub(x: f32, other: f32) -> f32 { + libm::fdimf(x, other) +} + +/// Experimental version of `cbrt` in `core`. See [`f32::cbrt`] for details. +/// +/// # Unspecified precision +/// +/// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and +/// can even differ within the same execution from one invocation to the next. +/// This function currently corresponds to the `cbrtf` from libc on Unix +/// and Windows. Note that this might change in the future. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 8.0f32; +/// +/// // x^(1/3) - 2 == 0 +/// let abs_difference = (f32::cbrt(x) - 2.0).abs(); +/// +/// assert!(abs_difference <= f32::EPSILON); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::cbrt`]: ../../std/primitive.f32.html#method.cbrt +#[inline] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn cbrt(x: f32) -> f32 { + libm::cbrtf(x) +} diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 81ab0f14c2bc..76c4e5d1a6f7 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -12,7 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -use crate::num::FpCategory; +use crate::num::{FpCategory, libm}; use crate::panic::const_assert; use crate::{intrinsics, mem}; @@ -1555,3 +1555,407 @@ impl f64 { intrinsics::frem_algebraic(self, rhs) } } + +/// Experimental version of `floor` in `core`. See [`f64::floor`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.7_f64; +/// let g = 3.0_f64; +/// let h = -3.7_f64; +/// +/// assert_eq!(f64::floor(f), 3.0); +/// assert_eq!(f64::floor(g), 3.0); +/// assert_eq!(f64::floor(h), -4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::floor`]: ../../std/primitive.f64.html#method.floor +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn floor(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf64(x) } +} + +/// Experimental version of `ceil` in `core`. See [`f64::ceil`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.01_f64; +/// let g = 4.0_f64; +/// +/// assert_eq!(f64::ceil(f), 4.0); +/// assert_eq!(f64::ceil(g), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::ceil`]: ../../std/primitive.f64.html#method.ceil +#[inline] +#[doc(alias = "ceiling")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn ceil(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf64(x) } +} + +/// Experimental version of `round` in `core`. See [`f64::round`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.3_f64; +/// let g = -3.3_f64; +/// let h = -3.7_f64; +/// let i = 3.5_f64; +/// let j = 4.5_f64; +/// +/// assert_eq!(f64::round(f), 3.0); +/// assert_eq!(f64::round(g), -3.0); +/// assert_eq!(f64::round(h), -4.0); +/// assert_eq!(f64::round(i), 4.0); +/// assert_eq!(f64::round(j), 5.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::round`]: ../../std/primitive.f64.html#method.round +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf64(x) } +} + +/// Experimental version of `round_ties_even` in `core`. See [`f64::round_ties_even`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.3_f64; +/// let g = -3.3_f64; +/// let h = 3.5_f64; +/// let i = 4.5_f64; +/// +/// assert_eq!(f64::round_ties_even(f), 3.0); +/// assert_eq!(f64::round_ties_even(g), -3.0); +/// assert_eq!(f64::round_ties_even(h), 4.0); +/// assert_eq!(f64::round_ties_even(i), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::round_ties_even`]: ../../std/primitive.f64.html#method.round_ties_even +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round_ties_even(x: f64) -> f64 { + intrinsics::round_ties_even_f64(x) +} + +/// Experimental version of `trunc` in `core`. See [`f64::trunc`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.7_f64; +/// let g = 3.0_f64; +/// let h = -3.7_f64; +/// +/// assert_eq!(f64::trunc(f), 3.0); +/// assert_eq!(f64::trunc(g), 3.0); +/// assert_eq!(f64::trunc(h), -3.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::trunc`]: ../../std/primitive.f64.html#method.trunc +#[inline] +#[doc(alias = "truncate")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn trunc(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf64(x) } +} + +/// Experimental version of `fract` in `core`. See [`f64::fract`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 3.6_f64; +/// let y = -3.6_f64; +/// let abs_difference_x = (f64::fract(x) - 0.6).abs(); +/// let abs_difference_y = (f64::fract(y) - (-0.6)).abs(); +/// +/// assert!(abs_difference_x < 1e-10); +/// assert!(abs_difference_y < 1e-10); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::fract`]: ../../std/primitive.f64.html#method.fract +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn fract(x: f64) -> f64 { + x - trunc(x) +} + +/// Experimental version of `mul_add` in `core`. See [`f64::mul_add`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// # // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +/// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { +/// use core::f64; +/// +/// let m = 10.0_f64; +/// let x = 4.0_f64; +/// let b = 60.0_f64; +/// +/// assert_eq!(f64::mul_add(m, x, b), 100.0); +/// assert_eq!(m * x + b, 100.0); +/// +/// let one_plus_eps = 1.0_f64 + f64::EPSILON; +/// let one_minus_eps = 1.0_f64 - f64::EPSILON; +/// let minus_one = -1.0_f64; +/// +/// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. +/// assert_eq!(f64::mul_add(one_plus_eps, one_minus_eps, minus_one), -f64::EPSILON * f64::EPSILON); +/// // Different rounding with the non-fused multiply and add. +/// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); +/// # } +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::mul_add`]: ../../std/primitive.f64.html#method.mul_add +#[inline] +#[doc(alias = "fma", alias = "fusedMultiplyAdd")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn mul_add(x: f64, a: f64, b: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf64(x, a, b) } +} + +/// Experimental version of `div_euclid` in `core`. See [`f64::div_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let a: f64 = 7.0; +/// let b = 4.0; +/// assert_eq!(f64::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 +/// assert_eq!(f64::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 +/// assert_eq!(f64::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 +/// assert_eq!(f64::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::div_euclid`]: ../../std/primitive.f64.html#method.div_euclid +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn div_euclid(x: f64, rhs: f64) -> f64 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q +} + +/// Experimental version of `rem_euclid` in `core`. See [`f64::rem_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let a: f64 = 7.0; +/// let b = 4.0; +/// assert_eq!(f64::rem_euclid(a, b), 3.0); +/// assert_eq!(f64::rem_euclid(-a, b), 1.0); +/// assert_eq!(f64::rem_euclid(a, -b), 3.0); +/// assert_eq!(f64::rem_euclid(-a, -b), 1.0); +/// // limitation due to round-off error +/// assert!(f64::rem_euclid(-f64::EPSILON, 3.0) != 0.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::rem_euclid`]: ../../std/primitive.f64.html#method.rem_euclid +#[inline] +#[doc(alias = "modulo", alias = "mod")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn rem_euclid(x: f64, rhs: f64) -> f64 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } +} + +/// Experimental version of `powi` in `core`. See [`f64::powi`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 2.0_f64; +/// let abs_difference = (f64::powi(x, 2) - (x * x)).abs(); +/// assert!(abs_difference <= f64::EPSILON); +/// +/// assert_eq!(f64::powi(f64::NAN, 0), 1.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::powi`]: ../../std/primitive.f64.html#method.powi +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn powi(x: f64, n: i32) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif64(x, n) } +} + +/// Experimental version of `sqrt` in `core`. See [`f64::sqrt`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let positive = 4.0_f64; +/// let negative = -4.0_f64; +/// let negative_zero = -0.0_f64; +/// +/// assert_eq!(f64::sqrt(positive), 2.0); +/// assert!(f64::sqrt(negative).is_nan()); +/// assert_eq!(f64::sqrt(negative_zero), negative_zero); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::sqrt`]: ../../std/primitive.f64.html#method.sqrt +#[inline] +#[doc(alias = "squareRoot")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn sqrt(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf64(x) } +} + +/// Experimental version of `abs_sub` in `core`. See [`f64::abs_sub`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 3.0_f64; +/// let y = -3.0_f64; +/// +/// let abs_difference_x = (f64::abs_sub(x, 1.0) - 2.0).abs(); +/// let abs_difference_y = (f64::abs_sub(y, 1.0) - 0.0).abs(); +/// +/// assert!(abs_difference_x < 1e-10); +/// assert!(abs_difference_y < 1e-10); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::abs_sub`]: ../../std/primitive.f64.html#method.abs_sub +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[deprecated( + since = "1.10.0", + note = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ + known as `fdim` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdim`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too)." +)] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn abs_sub(x: f64, other: f64) -> f64 { + libm::fdim(x, other) +} + +/// Experimental version of `cbrt` in `core`. See [`f64::cbrt`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 8.0_f64; +/// +/// // x^(1/3) - 2 == 0 +/// let abs_difference = (f64::cbrt(x) - 2.0).abs(); +/// +/// assert!(abs_difference < 1e-10); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::cbrt`]: ../../std/primitive.f64.html#method.cbrt +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn cbrt(x: f64) -> f64 { + libm::cbrt(x) +} diff --git a/library/core/src/num/flt2dec/decoder.rs b/library/core/src/num/flt2dec/decoder.rs index 40b3aae24a53..bd6e2cdbafec 100644 --- a/library/core/src/num/flt2dec/decoder.rs +++ b/library/core/src/num/flt2dec/decoder.rs @@ -45,6 +45,13 @@ pub trait DecodableFloat: RawFloat + Copy { fn min_pos_norm_value() -> Self; } +#[cfg(target_has_reliable_f16)] +impl DecodableFloat for f16 { + fn min_pos_norm_value() -> Self { + f16::MIN_POSITIVE + } +} + impl DecodableFloat for f32 { fn min_pos_norm_value() -> Self { f32::MIN_POSITIVE diff --git a/library/core/src/num/libm.rs b/library/core/src/num/libm.rs new file mode 100644 index 000000000000..aeabb0872309 --- /dev/null +++ b/library/core/src/num/libm.rs @@ -0,0 +1,11 @@ +//! Bindings to math functions provided by the system `libm` or by the `libm` crate, exposed +//! via `compiler-builtins`. + +// SAFETY: These symbols have standard interfaces in C and are defined by `libm`, or are +// provided by `compiler-builtins` on unsupported platforms. +unsafe extern "C" { + pub(crate) safe fn cbrt(n: f64) -> f64; + pub(crate) safe fn cbrtf(n: f32) -> f32; + pub(crate) safe fn fdim(a: f64, b: f64) -> f64; + pub(crate) safe fn fdimf(a: f32, b: f32) -> f32; +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index ecc1c7bf9021..5c73bddbef2d 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -46,6 +46,7 @@ mod uint_macros; // import uint_impl! mod error; mod int_log10; mod int_sqrt; +pub(crate) mod libm; mod nonzero; mod overflow_panic; mod saturating; @@ -492,6 +493,26 @@ impl u8 { ascii::Char::from_u8(*self) } + /// Converts this byte to an [ASCII character](ascii::Char), without + /// checking whether or not it's valid. + /// + /// # Safety + /// + /// This byte must be valid ASCII, or else this is UB. + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the byte is valid ASCII", + (it: &u8 = self) => it.is_ascii() + ); + + // SAFETY: the caller promised that this byte is ASCII. + unsafe { ascii::Char::from_u8_unchecked(*self) } + } + /// Makes a copy of the value in its ASCII upper case equivalent. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index ecfa723722d0..257424b355fb 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -12,11 +12,11 @@ //! "pinned," in that it has been permanently (until the end of its lifespan) attached to its //! location in memory, as though pinned to a pinboard. Pinning a value is an incredibly useful //! building block for [`unsafe`] code to be able to reason about whether a raw pointer to the -//! pinned value is still valid. [As we'll see later][drop-guarantee], this is necessarily from the -//! time the value is first pinned until the end of its lifespan. This concept of "pinning" is -//! necessary to implement safe interfaces on top of things like self-referential types and -//! intrusive data structures which cannot currently be modeled in fully safe Rust using only -//! borrow-checked [references][reference]. +//! pinned value is still valid. [As we'll see later][drop-guarantee], once a value is pinned, +//! it is necessarily valid at its memory location until the end of its lifespan. This concept +//! of "pinning" is necessary to implement safe interfaces on top of things like self-referential +//! types and intrusive data structures which cannot currently be modeled in fully safe Rust using +//! only borrow-checked [references][reference]. //! //! "Pinning" allows us to put a *value* which exists at some location in memory into a state where //! safe code cannot *move* that value to a different location in memory or otherwise invalidate it diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index d05fb6a6d319..8b31328de047 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -262,7 +262,8 @@ impl NonNull { } /// Converts a reference to a `NonNull` pointer. - #[unstable(feature = "non_null_from_ref", issue = "130823")] + #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn from_ref(r: &T) -> Self { // SAFETY: A reference cannot be null. @@ -270,7 +271,8 @@ impl NonNull { } /// Converts a mutable reference to a `NonNull` pointer. - #[unstable(feature = "non_null_from_ref", issue = "130823")] + #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn from_mut(r: &mut T) -> Self { // SAFETY: A mutable reference cannot be null. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 9e7e949b722d..e505e2280953 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -17,6 +17,7 @@ use self::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::char::{self, EscapeDebugExtArgs}; use crate::ops::Range; use crate::slice::{self, SliceIndex}; +use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, mem}; pub mod pattern; @@ -2634,6 +2635,27 @@ impl str { self.as_bytes().as_ascii() } + /// Converts this string slice into a slice of [ASCII characters](ascii::Char), + /// without checking whether they are valid. + /// + /// # Safety + /// + /// Every character in this string must be ASCII, or else this is UB. + #[unstable(feature = "ascii_char", issue = "110998")] + #[must_use] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char] { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the string is valid ASCII", + (it: &str = self) => it.is_ascii() + ); + + // SAFETY: the caller promised that every byte of this string slice + // is ASCII. + unsafe { self.as_bytes().as_ascii_unchecked() } + } + /// Checks that two strings are an ASCII case-insensitive match. /// /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, diff --git a/library/coretests/Cargo.toml b/library/coretests/Cargo.toml index 7656388d24be..e0ddcd466aea 100644 --- a/library/coretests/Cargo.toml +++ b/library/coretests/Cargo.toml @@ -26,3 +26,14 @@ test = true [dev-dependencies] rand = { version = "0.9.0", default-features = false } rand_xorshift = { version = "0.4.0", default-features = false } + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', +] diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index b6d18f1ec382..30ccbbc32031 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -325,7 +325,7 @@ fn array_map_drop_safety() { let success = std::panic::catch_unwind(|| { let items = [0; 10]; let mut nth = 0; - items.map(|_| { + let _ = items.map(|_| { assert!(nth < num_to_create); nth += 1; DropCounter diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs new file mode 100644 index 000000000000..12cf651f03f4 --- /dev/null +++ b/library/coretests/tests/floats/f128.rs @@ -0,0 +1,790 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(target_has_reliable_f128)] + +use std::f128::consts; +use std::num::FpCategory as Fp; +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +use std::ops::Rem; +use std::ops::{Add, Div, Mul, Sub}; + +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Smallest number +const TINY_BITS: u128 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u128 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; + +/// First pattern over the mantissa +const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u128 = 0x00005555555555555555555555555555; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f128_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f128, &f128) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); + }; +} + +#[test] +fn test_num_f128() { + // FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` + // function is available on all platforms. + let ten = 10f128; + let two = 2f128; + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); +} + +// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support +// the intrinsics. + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_num_f128_rem() { + let ten = 10f128; + let two = 2f128; + assert_eq!(ten.rem(two), ten % two); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f128 = f128::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f128::MANTISSA_DIGITS - 2)) != 0); +} + +#[test] +fn test_infinity() { + let inf: f128 = f128::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f128 = 0.0f128; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f128 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f128 = 1.0f128; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f128.is_nan()); + assert!(!5.3f128.is_nan()); + assert!(!(-10.732f128).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f128.is_infinite()); + assert!(!42.8f128.is_infinite()); + assert!(!(-109.2f128).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f128.is_finite()); + assert!(42.8f128.is_finite()); + assert!((-109.2f128).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f128.is_normal()); + assert!(1e-4931f128.is_normal()); + assert!(!1e-4932f128.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f128.classify(), Fp::Normal); + assert_eq!(1e-4931f128.classify(), Fp::Normal); + assert_eq!(1e-4932f128.classify(), Fp::Subnormal); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_abs() { + assert_eq!(f128::INFINITY.abs(), f128::INFINITY); + assert_eq!(1f128.abs(), 1f128); + assert_eq!(0f128.abs(), 0f128); + assert_eq!((-0f128).abs(), 0f128); + assert_eq!((-1f128).abs(), 1f128); + assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); + assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); + assert!(f128::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f128::INFINITY.is_sign_positive()); + assert!(1f128.is_sign_positive()); + assert!(0f128.is_sign_positive()); + assert!(!(-0f128).is_sign_positive()); + assert!(!(-1f128).is_sign_positive()); + assert!(!f128::NEG_INFINITY.is_sign_positive()); + assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); + assert!(f128::NAN.is_sign_positive()); + assert!(!(-f128::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f128::INFINITY.is_sign_negative()); + assert!(!1f128.is_sign_negative()); + assert!(!0f128.is_sign_negative()); + assert!((-0f128).is_sign_negative()); + assert!((-1f128).is_sign_negative()); + assert!(f128::NEG_INFINITY.is_sign_negative()); + assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); + assert!(!f128::NAN.is_sign_negative()); + assert!((-f128::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); + assert_f128_biteq!(f128::MIN.next_up(), -max_down); + assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); + assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f128_biteq!((-tiny_up).next_up(), -tiny); + assert_f128_biteq!((-tiny).next_up(), -0.0f128); + assert_f128_biteq!((-0.0f128).next_up(), tiny); + assert_f128_biteq!(0.0f128.next_up(), tiny); + assert_f128_biteq!(tiny.next_up(), tiny_up); + assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); + assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_up(), nan0); + assert_f128_biteq!(nan1.next_up(), nan1); + assert_f128_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!((-max_down).next_down(), f128::MIN); + assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); + assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f128_biteq!((-tiny).next_down(), -tiny_up); + assert_f128_biteq!((-0.0f128).next_down(), -tiny); + assert_f128_biteq!((0.0f128).next_down(), -tiny); + assert_f128_biteq!(tiny.next_down(), 0.0f128); + assert_f128_biteq!(tiny_up.next_down(), tiny); + assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); + assert_f128_biteq!(f128::MAX.next_down(), max_down); + assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_down(), nan0); + assert_f128_biteq!(nan1.next_down(), nan1); + assert_f128_biteq!(nan2.next_down(), nan2); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_recip() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.recip(), 1.0); + assert_eq!(2.0f128.recip(), 0.5); + assert_eq!((-0.4f128).recip(), -2.5); + assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +fn test_to_degrees() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_degrees(), 0.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_radians(), 0.0); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); + // check approx rather than exact because round trip for pi doesn't fall on an exactly + // representable value (unlike `f32` and `f64`). + assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); + assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); + assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; + assert!(f128::from_bits(masked_nan1).is_nan()); + assert!(f128::from_bits(masked_nan2).is_nan()); + + assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f128.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f128.clamp(f128::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f128.clamp(3.0, f128::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u128 { + 1 << (f128::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f128 { + // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) + // } + + // fn max_subnorm() -> f128 { + // f128::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f128 { + f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f128 { + f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} + +#[test] +fn test_algebraic() { + let a: f128 = 123.0; + let b: f128 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps); + assert_approx_eq!(a.algebraic_div(b), a / b, eps); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps); +} + +#[test] +fn test_from() { + assert_eq!(f128::from(false), 0.0); + assert_eq!(f128::from(true), 1.0); + assert_eq!(f128::from(u8::MIN), 0.0); + assert_eq!(f128::from(42_u8), 42.0); + assert_eq!(f128::from(u8::MAX), 255.0); + assert_eq!(f128::from(i8::MIN), -128.0); + assert_eq!(f128::from(42_i8), 42.0); + assert_eq!(f128::from(i8::MAX), 127.0); + assert_eq!(f128::from(u16::MIN), 0.0); + assert_eq!(f128::from(42_u16), 42.0); + assert_eq!(f128::from(u16::MAX), 65535.0); + assert_eq!(f128::from(i16::MIN), -32768.0); + assert_eq!(f128::from(42_i16), 42.0); + assert_eq!(f128::from(i16::MAX), 32767.0); + assert_eq!(f128::from(u32::MIN), 0.0); + assert_eq!(f128::from(42_u32), 42.0); + assert_eq!(f128::from(u32::MAX), 4294967295.0); + assert_eq!(f128::from(i32::MIN), -2147483648.0); + assert_eq!(f128::from(42_i32), 42.0); + assert_eq!(f128::from(i32::MAX), 2147483647.0); + // FIXME(f16_f128): Uncomment these tests once the From<{u64,i64}> impls are added. + // assert_eq!(f128::from(u64::MIN), 0.0); + // assert_eq!(f128::from(42_u64), 42.0); + // assert_eq!(f128::from(u64::MAX), 18446744073709551615.0); + // assert_eq!(f128::from(i64::MIN), -9223372036854775808.0); + // assert_eq!(f128::from(42_i64), 42.0); + // assert_eq!(f128::from(i64::MAX), 9223372036854775807.0); +} diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs new file mode 100644 index 000000000000..db98181226c8 --- /dev/null +++ b/library/coretests/tests/floats/f16.rs @@ -0,0 +1,753 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(target_has_reliable_f16)] + +use std::f16::consts; +use std::num::FpCategory as Fp; + +/// Tolerance for results on the order of 10.0e-2 +#[allow(unused)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[allow(unused)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[allow(unused)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[allow(unused)] +const TOL_P4: f16 = 10.0; + +/// Smallest number +const TINY_BITS: u16 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u16 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u16 = 0x7bfe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u16 = 0x0400; + +/// First pattern over the mantissa +const NAN_MASK1: u16 = 0x02aa; + +/// Second pattern over the mantissa +const NAN_MASK2: u16 = 0x0155; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f16_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f16, &f16) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); + }; +} + +#[test] +fn test_num_f16() { + super::test_num(10f16, 2f16); +} + +// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support +// the intrinsics. + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f16 = f16::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f16::MANTISSA_DIGITS - 2)) != 0); +} + +#[test] +fn test_infinity() { + let inf: f16 = f16::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f16 = 0.0f16; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f16 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f16 = 1.0f16; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f16.is_nan()); + assert!(!5.3f16.is_nan()); + assert!(!(-10.732f16).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f16.is_infinite()); + assert!(!42.8f16.is_infinite()); + assert!(!(-109.2f16).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f16.is_finite()); + assert!(42.8f16.is_finite()); + assert!((-109.2f16).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f16.is_normal()); + assert!(1e-4f16.is_normal()); + assert!(!1e-5f16.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f16.classify(), Fp::Normal); + assert_eq!(1e-4f16.classify(), Fp::Normal); + assert_eq!(1e-5f16.classify(), Fp::Subnormal); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_abs() { + assert_eq!(f16::INFINITY.abs(), f16::INFINITY); + assert_eq!(1f16.abs(), 1f16); + assert_eq!(0f16.abs(), 0f16); + assert_eq!((-0f16).abs(), 0f16); + assert_eq!((-1f16).abs(), 1f16); + assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); + assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); + assert!(f16::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f16::INFINITY.is_sign_positive()); + assert!(1f16.is_sign_positive()); + assert!(0f16.is_sign_positive()); + assert!(!(-0f16).is_sign_positive()); + assert!(!(-1f16).is_sign_positive()); + assert!(!f16::NEG_INFINITY.is_sign_positive()); + assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); + assert!(f16::NAN.is_sign_positive()); + assert!(!(-f16::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f16::INFINITY.is_sign_negative()); + assert!(!1f16.is_sign_negative()); + assert!(!0f16.is_sign_negative()); + assert!((-0f16).is_sign_negative()); + assert!((-1f16).is_sign_negative()); + assert!(f16::NEG_INFINITY.is_sign_negative()); + assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); + assert!(!f16::NAN.is_sign_negative()); + assert!((-f16::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); + assert_f16_biteq!(f16::MIN.next_up(), -max_down); + assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); + assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f16_biteq!((-tiny_up).next_up(), -tiny); + assert_f16_biteq!((-tiny).next_up(), -0.0f16); + assert_f16_biteq!((-0.0f16).next_up(), tiny); + assert_f16_biteq!(0.0f16.next_up(), tiny); + assert_f16_biteq!(tiny.next_up(), tiny_up); + assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); + assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_up(), nan0); + assert_f16_biteq!(nan1.next_up(), nan1); + assert_f16_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!((-max_down).next_down(), f16::MIN); + assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); + assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f16_biteq!((-tiny).next_down(), -tiny_up); + assert_f16_biteq!((-0.0f16).next_down(), -tiny); + assert_f16_biteq!((0.0f16).next_down(), -tiny); + assert_f16_biteq!(tiny.next_down(), 0.0f16); + assert_f16_biteq!(tiny_up.next_down(), tiny); + assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); + assert_f16_biteq!(f16::MAX.next_down(), max_down); + assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_down(), nan0); + assert_f16_biteq!(nan1.next_down(), nan1); + assert_f16_biteq!(nan2.next_down(), nan2); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_recip() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.recip(), 1.0); + assert_eq!(2.0f16.recip(), 0.5); + assert_eq!((-0.4f16).recip(), -2.5); + assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_powi() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powi(1), 1.0); + assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + assert_eq!(8.3f16.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +fn test_to_degrees() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_degrees(), 0.0); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_radians(), 0.0); + assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f16).to_bits(), 0x3c00); + assert_eq!((12.5f16).to_bits(), 0x4a40); + assert_eq!((1337f16).to_bits(), 0x6539); + assert_eq!((-14.25f16).to_bits(), 0xcb20); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; + assert!(f16::from_bits(masked_nan1).is_nan()); + assert!(f16::from_bits(masked_nan2).is_nan()); + + assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f16.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f16.clamp(f16::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f16.clamp(3.0, f16::NAN); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u16 { + 1 << (f16::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f16 { + f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) + } + + fn max_subnorm() -> f16 { + f16::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f16 { + f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f16 { + f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} + +#[test] +fn test_algebraic() { + let a: f16 = 123.0; + let b: f16 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps_add = if cfg!(miri) { 1e1 } else { 0.0 }; + let eps_mul = if cfg!(miri) { 1e3 } else { 0.0 }; + let eps_div = if cfg!(miri) { 1e0 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); + assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); +} + +#[test] +fn test_from() { + assert_eq!(f16::from(false), 0.0); + assert_eq!(f16::from(true), 1.0); + assert_eq!(f16::from(u8::MIN), 0.0); + assert_eq!(f16::from(42_u8), 42.0); + assert_eq!(f16::from(u8::MAX), 255.0); + assert_eq!(f16::from(i8::MIN), -128.0); + assert_eq!(f16::from(42_i8), 42.0); + assert_eq!(f16::from(i8::MAX), 127.0); +} diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs new file mode 100644 index 000000000000..9b551643bae2 --- /dev/null +++ b/library/coretests/tests/floats/f32.rs @@ -0,0 +1,702 @@ +use core::f32; +use core::f32::consts; +use core::num::FpCategory as Fp; + +/// Smallest number +const TINY_BITS: u32 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u32 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; + +/// First pattern over the mantissa +const NAN_MASK1: u32 = 0x002a_aaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u32 = 0x0055_5555; + +#[allow(unused_macros)] +macro_rules! assert_f32_biteq { + ($left : expr, $right : expr) => { + let l: &f32 = &$left; + let r: &f32 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); + }; +} + +#[test] +fn test_num_f32() { + super::test_num(10f32, 2f32); +} + +#[test] +fn test_min_nan() { + assert_eq!(f32::NAN.min(2.0), 2.0); + assert_eq!(2.0f32.min(f32::NAN), 2.0); +} + +#[test] +fn test_max_nan() { + assert_eq!(f32::NAN.max(2.0), 2.0); + assert_eq!(2.0f32.max(f32::NAN), 2.0); +} + +#[test] +fn test_minimum() { + assert!(f32::NAN.minimum(2.0).is_nan()); + assert!(2.0f32.minimum(f32::NAN).is_nan()); +} + +#[test] +fn test_maximum() { + assert!(f32::NAN.maximum(2.0).is_nan()); + assert!(2.0f32.maximum(f32::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f32 = f32::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(!nan.is_normal()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f32::MANTISSA_DIGITS - 2)) != 0); +} + +#[test] +fn test_infinity() { + let inf: f32 = f32::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f32 = 0.0f32; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f32 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f32 = 1.0f32; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f32.is_nan()); + assert!(!5.3f32.is_nan()); + assert!(!(-10.732f32).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f32.is_infinite()); + assert!(!42.8f32.is_infinite()); + assert!(!(-109.2f32).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f32.is_finite()); + assert!(42.8f32.is_finite()); + assert!((-109.2f32).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let zero: f32 = 0.0f32; + let neg_zero: f32 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f32.is_normal()); + assert!(1e-37f32.is_normal()); + assert!(!1e-38f32.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let zero: f32 = 0.0f32; + let neg_zero: f32 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f32.classify(), Fp::Normal); + assert_eq!(1e-37f32.classify(), Fp::Normal); + assert_eq!(1e-38f32.classify(), Fp::Subnormal); +} + +#[test] +fn test_floor() { + assert_approx_eq!(f32::floor(1.0f32), 1.0f32); + assert_approx_eq!(f32::floor(1.3f32), 1.0f32); + assert_approx_eq!(f32::floor(1.5f32), 1.0f32); + assert_approx_eq!(f32::floor(1.7f32), 1.0f32); + assert_approx_eq!(f32::floor(0.0f32), 0.0f32); + assert_approx_eq!(f32::floor(-0.0f32), -0.0f32); + assert_approx_eq!(f32::floor(-1.0f32), -1.0f32); + assert_approx_eq!(f32::floor(-1.3f32), -2.0f32); + assert_approx_eq!(f32::floor(-1.5f32), -2.0f32); + assert_approx_eq!(f32::floor(-1.7f32), -2.0f32); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(f32::ceil(1.0f32), 1.0f32); + assert_approx_eq!(f32::ceil(1.3f32), 2.0f32); + assert_approx_eq!(f32::ceil(1.5f32), 2.0f32); + assert_approx_eq!(f32::ceil(1.7f32), 2.0f32); + assert_approx_eq!(f32::ceil(0.0f32), 0.0f32); + assert_approx_eq!(f32::ceil(-0.0f32), -0.0f32); + assert_approx_eq!(f32::ceil(-1.0f32), -1.0f32); + assert_approx_eq!(f32::ceil(-1.3f32), -1.0f32); + assert_approx_eq!(f32::ceil(-1.5f32), -1.0f32); + assert_approx_eq!(f32::ceil(-1.7f32), -1.0f32); +} + +#[test] +fn test_round() { + assert_approx_eq!(f32::round(2.5f32), 3.0f32); + assert_approx_eq!(f32::round(1.0f32), 1.0f32); + assert_approx_eq!(f32::round(1.3f32), 1.0f32); + assert_approx_eq!(f32::round(1.5f32), 2.0f32); + assert_approx_eq!(f32::round(1.7f32), 2.0f32); + assert_approx_eq!(f32::round(0.0f32), 0.0f32); + assert_approx_eq!(f32::round(-0.0f32), -0.0f32); + assert_approx_eq!(f32::round(-1.0f32), -1.0f32); + assert_approx_eq!(f32::round(-1.3f32), -1.0f32); + assert_approx_eq!(f32::round(-1.5f32), -2.0f32); + assert_approx_eq!(f32::round(-1.7f32), -2.0f32); +} + +#[test] +fn test_round_ties_even() { + assert_approx_eq!(f32::round_ties_even(2.5f32), 2.0f32); + assert_approx_eq!(f32::round_ties_even(1.0f32), 1.0f32); + assert_approx_eq!(f32::round_ties_even(1.3f32), 1.0f32); + assert_approx_eq!(f32::round_ties_even(1.5f32), 2.0f32); + assert_approx_eq!(f32::round_ties_even(1.7f32), 2.0f32); + assert_approx_eq!(f32::round_ties_even(0.0f32), 0.0f32); + assert_approx_eq!(f32::round_ties_even(-0.0f32), -0.0f32); + assert_approx_eq!(f32::round_ties_even(-1.0f32), -1.0f32); + assert_approx_eq!(f32::round_ties_even(-1.3f32), -1.0f32); + assert_approx_eq!(f32::round_ties_even(-1.5f32), -2.0f32); + assert_approx_eq!(f32::round_ties_even(-1.7f32), -2.0f32); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(f32::trunc(1.0f32), 1.0f32); + assert_approx_eq!(f32::trunc(1.3f32), 1.0f32); + assert_approx_eq!(f32::trunc(1.5f32), 1.0f32); + assert_approx_eq!(f32::trunc(1.7f32), 1.0f32); + assert_approx_eq!(f32::trunc(0.0f32), 0.0f32); + assert_approx_eq!(f32::trunc(-0.0f32), -0.0f32); + assert_approx_eq!(f32::trunc(-1.0f32), -1.0f32); + assert_approx_eq!(f32::trunc(-1.3f32), -1.0f32); + assert_approx_eq!(f32::trunc(-1.5f32), -1.0f32); + assert_approx_eq!(f32::trunc(-1.7f32), -1.0f32); +} + +#[test] +fn test_fract() { + assert_approx_eq!(f32::fract(1.0f32), 0.0f32); + assert_approx_eq!(f32::fract(1.3f32), 0.3f32); + assert_approx_eq!(f32::fract(1.5f32), 0.5f32); + assert_approx_eq!(f32::fract(1.7f32), 0.7f32); + assert_approx_eq!(f32::fract(0.0f32), 0.0f32); + assert_approx_eq!(f32::fract(-0.0f32), -0.0f32); + assert_approx_eq!(f32::fract(-1.0f32), -0.0f32); + assert_approx_eq!(f32::fract(-1.3f32), -0.3f32); + assert_approx_eq!(f32::fract(-1.5f32), -0.5f32); + assert_approx_eq!(f32::fract(-1.7f32), -0.7f32); +} + +#[test] +fn test_abs() { + assert_eq!(f32::INFINITY.abs(), f32::INFINITY); + assert_eq!(1f32.abs(), 1f32); + assert_eq!(0f32.abs(), 0f32); + assert_eq!((-0f32).abs(), 0f32); + assert_eq!((-1f32).abs(), 1f32); + assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); + assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); + assert!(f32::NAN.abs().is_nan()); +} + +#[test] +fn test_signum() { + assert_eq!(f32::INFINITY.signum(), 1f32); + assert_eq!(1f32.signum(), 1f32); + assert_eq!(0f32.signum(), 1f32); + assert_eq!((-0f32).signum(), -1f32); + assert_eq!((-1f32).signum(), -1f32); + assert_eq!(f32::NEG_INFINITY.signum(), -1f32); + assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); + assert!(f32::NAN.signum().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f32::INFINITY.is_sign_positive()); + assert!(1f32.is_sign_positive()); + assert!(0f32.is_sign_positive()); + assert!(!(-0f32).is_sign_positive()); + assert!(!(-1f32).is_sign_positive()); + assert!(!f32::NEG_INFINITY.is_sign_positive()); + assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); + assert!(f32::NAN.is_sign_positive()); + assert!(!(-f32::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f32::INFINITY.is_sign_negative()); + assert!(!1f32.is_sign_negative()); + assert!(!0f32.is_sign_negative()); + assert!((-0f32).is_sign_negative()); + assert!((-1f32).is_sign_negative()); + assert!(f32::NEG_INFINITY.is_sign_negative()); + assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); + assert!(!f32::NAN.is_sign_negative()); + assert!((-f32::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); + assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); + assert_f32_biteq!(f32::MIN.next_up(), -max_down); + assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); + assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f32_biteq!((-tiny_up).next_up(), -tiny); + assert_f32_biteq!((-tiny).next_up(), -0.0f32); + assert_f32_biteq!((-0.0f32).next_up(), tiny); + assert_f32_biteq!(0.0f32.next_up(), tiny); + assert_f32_biteq!(tiny.next_up(), tiny_up); + assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); + assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); + assert_f32_biteq!(nan0.next_up(), nan0); + assert_f32_biteq!(nan1.next_up(), nan1); + assert_f32_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); + assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!((-max_down).next_down(), f32::MIN); + assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); + assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f32_biteq!((-tiny).next_down(), -tiny_up); + assert_f32_biteq!((-0.0f32).next_down(), -tiny); + assert_f32_biteq!((0.0f32).next_down(), -tiny); + assert_f32_biteq!(tiny.next_down(), 0.0f32); + assert_f32_biteq!(tiny_up.next_down(), tiny); + assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); + assert_f32_biteq!(f32::MAX.next_down(), max_down); + assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); + assert_f32_biteq!(nan0.next_down(), nan0); + assert_f32_biteq!(nan1.next_down(), nan1); + assert_f32_biteq!(nan2.next_down(), nan2); +} + +// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] +#[test] +fn test_mul_add() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(f32::mul_add(12.3f32, 4.5, 6.7), 62.05); + assert_approx_eq!(f32::mul_add(-12.3f32, -4.5, -6.7), 48.65); + assert_approx_eq!(f32::mul_add(0.0f32, 8.9, 1.2), 1.2); + assert_approx_eq!(f32::mul_add(3.4f32, -0.0, 5.6), 5.6); + assert!(f32::mul_add(nan, 7.8, 9.0).is_nan()); + assert_eq!(f32::mul_add(inf, 7.8, 9.0), inf); + assert_eq!(f32::mul_add(neg_inf, 7.8, 9.0), neg_inf); + assert_eq!(f32::mul_add(8.9f32, inf, 3.2), inf); + assert_eq!(f32::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); +} + +#[test] +fn test_recip() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.recip(), 1.0); + assert_eq!(2.0f32.recip(), 0.5); + assert_eq!((-0.4f32).recip(), -2.5); + assert_eq!(0.0f32.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_powi() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.powi(1), 1.0); + assert_approx_eq!((-3.1f32).powi(2), 9.61); + assert_approx_eq!(5.9f32.powi(-2), 0.028727); + assert_eq!(8.3f32.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +fn test_sqrt_domain() { + assert!(f32::NAN.sqrt().is_nan()); + assert!(f32::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f32).sqrt().is_nan()); + assert_eq!((-0.0f32).sqrt(), -0.0); + assert_eq!(0.0f32.sqrt(), 0.0); + assert_eq!(1.0f32.sqrt(), 1.0); + assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); +} + +#[test] +fn test_to_degrees() { + let pi: f32 = consts::PI; + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(0.0f32.to_degrees(), 0.0); + assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); + assert_eq!(pi.to_degrees(), 180.0); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f32 = consts::PI; + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(0.0f32.to_radians(), 0.0); + assert_approx_eq!(154.6f32.to_radians(), 2.698279); + assert_approx_eq!((-332.31f32).to_radians(), -5.799903); + assert_eq!(180.0f32.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f32).to_bits(), 0x3f800000); + assert_eq!((12.5f32).to_bits(), 0x41480000); + assert_eq!((1337f32).to_bits(), 0x44a72000); + assert_eq!((-14.25f32).to_bits(), 0xc1640000); + assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); + assert_approx_eq!(f32::from_bits(0x41480000), 12.5); + assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); + assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; + assert!(f32::from_bits(masked_nan1).is_nan()); + assert!(f32::from_bits(masked_nan2).is_nan()); + + assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f32.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f32.clamp(f32::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f32.clamp(3.0, f32::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u32 { + 1 << (f32::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f32 { + f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) + } + + fn max_subnorm() -> f32 { + f32::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f32 { + f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f32 { + f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} + +#[test] +fn test_algebraic() { + let a: f32 = 123.0; + let b: f32 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps_add = if cfg!(miri) { 1e-3 } else { 0.0 }; + let eps_mul = if cfg!(miri) { 1e-1 } else { 0.0 }; + let eps_div = if cfg!(miri) { 1e-4 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); + assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); +} diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs new file mode 100644 index 000000000000..988108371d73 --- /dev/null +++ b/library/coretests/tests/floats/f64.rs @@ -0,0 +1,682 @@ +use std::f64::consts; +use std::num::FpCategory as Fp; + +/// Smallest number +const TINY_BITS: u64 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u64 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; + +/// First pattern over the mantissa +const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u64 = 0x0005_5555_5555_5555; + +#[allow(unused_macros)] +macro_rules! assert_f64_biteq { + ($left : expr, $right : expr) => { + let l: &f64 = &$left; + let r: &f64 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); + }; +} + +#[test] +fn test_num_f64() { + super::test_num(10f64, 2f64); +} + +#[test] +fn test_min_nan() { + assert_eq!(f64::NAN.min(2.0), 2.0); + assert_eq!(2.0f64.min(f64::NAN), 2.0); +} + +#[test] +fn test_max_nan() { + assert_eq!(f64::NAN.max(2.0), 2.0); + assert_eq!(2.0f64.max(f64::NAN), 2.0); +} + +#[test] +fn test_nan() { + let nan: f64 = f64::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(!nan.is_normal()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f64::MANTISSA_DIGITS - 2)) != 0); +} + +#[test] +fn test_infinity() { + let inf: f64 = f64::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f64 = 0.0f64; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f64 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f64 = 1.0f64; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f64.is_nan()); + assert!(!5.3f64.is_nan()); + assert!(!(-10.732f64).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f64.is_infinite()); + assert!(!42.8f64.is_infinite()); + assert!(!(-109.2f64).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f64.is_finite()); + assert!(42.8f64.is_finite()); + assert!((-109.2f64).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let zero: f64 = 0.0f64; + let neg_zero: f64 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f64.is_normal()); + assert!(1e-307f64.is_normal()); + assert!(!1e-308f64.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let zero: f64 = 0.0f64; + let neg_zero: f64 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1e-307f64.classify(), Fp::Normal); + assert_eq!(1e-308f64.classify(), Fp::Subnormal); +} + +#[test] +fn test_floor() { + assert_approx_eq!(f64::floor(1.0f64), 1.0f64); + assert_approx_eq!(f64::floor(1.3f64), 1.0f64); + assert_approx_eq!(f64::floor(1.5f64), 1.0f64); + assert_approx_eq!(f64::floor(1.7f64), 1.0f64); + assert_approx_eq!(f64::floor(0.0f64), 0.0f64); + assert_approx_eq!(f64::floor(-0.0f64), -0.0f64); + assert_approx_eq!(f64::floor(-1.0f64), -1.0f64); + assert_approx_eq!(f64::floor(-1.3f64), -2.0f64); + assert_approx_eq!(f64::floor(-1.5f64), -2.0f64); + assert_approx_eq!(f64::floor(-1.7f64), -2.0f64); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(f64::ceil(1.0f64), 1.0f64); + assert_approx_eq!(f64::ceil(1.3f64), 2.0f64); + assert_approx_eq!(f64::ceil(1.5f64), 2.0f64); + assert_approx_eq!(f64::ceil(1.7f64), 2.0f64); + assert_approx_eq!(f64::ceil(0.0f64), 0.0f64); + assert_approx_eq!(f64::ceil(-0.0f64), -0.0f64); + assert_approx_eq!(f64::ceil(-1.0f64), -1.0f64); + assert_approx_eq!(f64::ceil(-1.3f64), -1.0f64); + assert_approx_eq!(f64::ceil(-1.5f64), -1.0f64); + assert_approx_eq!(f64::ceil(-1.7f64), -1.0f64); +} + +#[test] +fn test_round() { + assert_approx_eq!(f64::round(2.5f64), 3.0f64); + assert_approx_eq!(f64::round(1.0f64), 1.0f64); + assert_approx_eq!(f64::round(1.3f64), 1.0f64); + assert_approx_eq!(f64::round(1.5f64), 2.0f64); + assert_approx_eq!(f64::round(1.7f64), 2.0f64); + assert_approx_eq!(f64::round(0.0f64), 0.0f64); + assert_approx_eq!(f64::round(-0.0f64), -0.0f64); + assert_approx_eq!(f64::round(-1.0f64), -1.0f64); + assert_approx_eq!(f64::round(-1.3f64), -1.0f64); + assert_approx_eq!(f64::round(-1.5f64), -2.0f64); + assert_approx_eq!(f64::round(-1.7f64), -2.0f64); +} + +#[test] +fn test_round_ties_even() { + assert_approx_eq!(f64::round_ties_even(2.5f64), 2.0f64); + assert_approx_eq!(f64::round_ties_even(1.0f64), 1.0f64); + assert_approx_eq!(f64::round_ties_even(1.3f64), 1.0f64); + assert_approx_eq!(f64::round_ties_even(1.5f64), 2.0f64); + assert_approx_eq!(f64::round_ties_even(1.7f64), 2.0f64); + assert_approx_eq!(f64::round_ties_even(0.0f64), 0.0f64); + assert_approx_eq!(f64::round_ties_even(-0.0f64), -0.0f64); + assert_approx_eq!(f64::round_ties_even(-1.0f64), -1.0f64); + assert_approx_eq!(f64::round_ties_even(-1.3f64), -1.0f64); + assert_approx_eq!(f64::round_ties_even(-1.5f64), -2.0f64); + assert_approx_eq!(f64::round_ties_even(-1.7f64), -2.0f64); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(f64::trunc(1.0f64), 1.0f64); + assert_approx_eq!(f64::trunc(1.3f64), 1.0f64); + assert_approx_eq!(f64::trunc(1.5f64), 1.0f64); + assert_approx_eq!(f64::trunc(1.7f64), 1.0f64); + assert_approx_eq!(f64::trunc(0.0f64), 0.0f64); + assert_approx_eq!(f64::trunc(-0.0f64), -0.0f64); + assert_approx_eq!(f64::trunc(-1.0f64), -1.0f64); + assert_approx_eq!(f64::trunc(-1.3f64), -1.0f64); + assert_approx_eq!(f64::trunc(-1.5f64), -1.0f64); + assert_approx_eq!(f64::trunc(-1.7f64), -1.0f64); +} + +#[test] +fn test_fract() { + assert_approx_eq!(f64::fract(1.0f64), 0.0f64); + assert_approx_eq!(f64::fract(1.3f64), 0.3f64); + assert_approx_eq!(f64::fract(1.5f64), 0.5f64); + assert_approx_eq!(f64::fract(1.7f64), 0.7f64); + assert_approx_eq!(f64::fract(0.0f64), 0.0f64); + assert_approx_eq!(f64::fract(-0.0f64), -0.0f64); + assert_approx_eq!(f64::fract(-1.0f64), -0.0f64); + assert_approx_eq!(f64::fract(-1.3f64), -0.3f64); + assert_approx_eq!(f64::fract(-1.5f64), -0.5f64); + assert_approx_eq!(f64::fract(-1.7f64), -0.7f64); +} + +#[test] +fn test_abs() { + assert_eq!(f64::INFINITY.abs(), f64::INFINITY); + assert_eq!(1f64.abs(), 1f64); + assert_eq!(0f64.abs(), 0f64); + assert_eq!((-0f64).abs(), 0f64); + assert_eq!((-1f64).abs(), 1f64); + assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); + assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); + assert!(f64::NAN.abs().is_nan()); +} + +#[test] +fn test_signum() { + assert_eq!(f64::INFINITY.signum(), 1f64); + assert_eq!(1f64.signum(), 1f64); + assert_eq!(0f64.signum(), 1f64); + assert_eq!((-0f64).signum(), -1f64); + assert_eq!((-1f64).signum(), -1f64); + assert_eq!(f64::NEG_INFINITY.signum(), -1f64); + assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); + assert!(f64::NAN.signum().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f64::INFINITY.is_sign_positive()); + assert!(1f64.is_sign_positive()); + assert!(0f64.is_sign_positive()); + assert!(!(-0f64).is_sign_positive()); + assert!(!(-1f64).is_sign_positive()); + assert!(!f64::NEG_INFINITY.is_sign_positive()); + assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); + assert!(f64::NAN.is_sign_positive()); + assert!(!(-f64::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f64::INFINITY.is_sign_negative()); + assert!(!1f64.is_sign_negative()); + assert!(!0f64.is_sign_negative()); + assert!((-0f64).is_sign_negative()); + assert!((-1f64).is_sign_negative()); + assert!(f64::NEG_INFINITY.is_sign_negative()); + assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); + assert!(!f64::NAN.is_sign_negative()); + assert!((-f64::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); + assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); + assert_f64_biteq!(f64::MIN.next_up(), -max_down); + assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); + assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f64_biteq!((-tiny_up).next_up(), -tiny); + assert_f64_biteq!((-tiny).next_up(), -0.0f64); + assert_f64_biteq!((-0.0f64).next_up(), tiny); + assert_f64_biteq!(0.0f64.next_up(), tiny); + assert_f64_biteq!(tiny.next_up(), tiny_up); + assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); + assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); + assert_f64_biteq!(nan0.next_up(), nan0); + assert_f64_biteq!(nan1.next_up(), nan1); + assert_f64_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); + assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!((-max_down).next_down(), f64::MIN); + assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); + assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f64_biteq!((-tiny).next_down(), -tiny_up); + assert_f64_biteq!((-0.0f64).next_down(), -tiny); + assert_f64_biteq!((0.0f64).next_down(), -tiny); + assert_f64_biteq!(tiny.next_down(), 0.0f64); + assert_f64_biteq!(tiny_up.next_down(), tiny); + assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); + assert_f64_biteq!(f64::MAX.next_down(), max_down); + assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); + assert_f64_biteq!(nan0.next_down(), nan0); + assert_f64_biteq!(nan1.next_down(), nan1); + assert_f64_biteq!(nan2.next_down(), nan2); +} + +// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] +#[test] +fn test_mul_add() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); + assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); + assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); + assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f64.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +fn test_recip() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.recip(), 1.0); + assert_eq!(2.0f64.recip(), 0.5); + assert_eq!((-0.4f64).recip(), -2.5); + assert_eq!(0.0f64.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_powi() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.powi(1), 1.0); + assert_approx_eq!((-3.1f64).powi(2), 9.61); + assert_approx_eq!(5.9f64.powi(-2), 0.028727); + assert_eq!(8.3f64.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +fn test_sqrt_domain() { + assert!(f64::NAN.sqrt().is_nan()); + assert!(f64::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f64).sqrt().is_nan()); + assert_eq!((-0.0f64).sqrt(), -0.0); + assert_eq!(0.0f64.sqrt(), 0.0); + assert_eq!(1.0f64.sqrt(), 1.0); + assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); +} + +#[test] +fn test_to_degrees() { + let pi: f64 = consts::PI; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(0.0f64.to_degrees(), 0.0); + assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); + assert_eq!(pi.to_degrees(), 180.0); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); +} + +#[test] +fn test_to_radians() { + let pi: f64 = consts::PI; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(0.0f64.to_radians(), 0.0); + assert_approx_eq!(154.6f64.to_radians(), 2.698279); + assert_approx_eq!((-332.31f64).to_radians(), -5.799903); + assert_eq!(180.0f64.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f64).to_bits(), 0x3ff0000000000000); + assert_eq!((12.5f64).to_bits(), 0x4029000000000000); + assert_eq!((1337f64).to_bits(), 0x4094e40000000000); + assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); + assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); + assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); + assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); + assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; + assert!(f64::from_bits(masked_nan1).is_nan()); + assert!(f64::from_bits(masked_nan2).is_nan()); + + assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f64.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f64.clamp(f64::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f64.clamp(3.0, f64::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u64 { + 1 << (f64::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f64 { + f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) + } + + fn max_subnorm() -> f64 { + f64::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f64 { + f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f64 { + f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} + +#[test] +fn test_algebraic() { + let a: f64 = 123.0; + let b: f64 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps); + assert_approx_eq!(a.algebraic_div(b), a / b, eps); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps); +} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs new file mode 100644 index 000000000000..7de34271ad05 --- /dev/null +++ b/library/coretests/tests/floats/mod.rs @@ -0,0 +1,40 @@ +use std::fmt; +use std::ops::{Add, Div, Mul, Rem, Sub}; + +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!( + diff <= $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", + lim = $lim + ); + }}; +} + +/// Helper function for testing numeric operations +pub fn test_num(ten: T, two: T) +where + T: PartialEq + + Add + + Sub + + Mul + + Div + + Rem + + fmt::Debug + + Copy, +{ + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); +} + +mod f128; +mod f16; +mod f32; +mod f64; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0575375cf4f0..b98e52718f60 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -12,10 +12,12 @@ #![feature(async_iterator)] #![feature(bigint_helper_methods)] #![feature(bstr)] +#![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(const_eval_select)] #![feature(const_trait_impl)] +#![feature(core_float_math)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)] #![feature(core_io_borrowed_buf)] @@ -29,6 +31,10 @@ #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(extern_types)] +#![feature(f128)] +#![feature(f16)] +#![feature(float_algebraic)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] @@ -144,6 +150,7 @@ mod cmp; mod const_ptr; mod convert; mod ffi; +mod floats; mod fmt; mod future; mod hash; diff --git a/library/coretests/tests/num/dec2flt/decimal.rs b/library/coretests/tests/num/dec2flt/decimal.rs index 1fa06de692e0..f759e1dbde6c 100644 --- a/library/coretests/tests/num/dec2flt/decimal.rs +++ b/library/coretests/tests/num/dec2flt/decimal.rs @@ -7,6 +7,20 @@ const FPATHS_F32: &[FPath] = const FPATHS_F64: &[FPath] = &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn check_fast_path_f16() { + const FPATHS_F16: &[FPath] = + &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; + for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F16.iter().copied() { + let dec = Decimal { exponent, mantissa, negative, many_digits }; + let actual = dec.try_fast_path::(); + + assert_eq!(actual, expected); + } +} + #[test] fn check_fast_path_f32() { for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F32.iter().copied() { diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index b5afd3e3b243..264de061be98 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -1,5 +1,24 @@ use core::num::dec2flt::float::RawFloat; +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_integer_decode() { + assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1)); + assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1)); + #[cfg(not(miri))] // miri doesn't have powf16 + assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1)); + assert_eq!(0f16.integer_decode(), (0, -25, 1)); + assert_eq!((-0f16).integer_decode(), (0, -25, -1)); + assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1)); + assert_eq!(f16::NEG_INFINITY.integer_decode(), (1 << 10, 6, -1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode(); + assert_eq!((nan_m, nan_p), (1536, 6)); +} + #[test] fn test_f32_integer_decode() { assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1)); @@ -34,6 +53,27 @@ fn test_f64_integer_decode() { /* Sanity checks of computed magic numbers */ +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_consts() { + assert_eq!(::INFINITY, f16::INFINITY); + assert_eq!(::NEG_INFINITY, -f16::INFINITY); + assert_eq!(::NAN.to_bits(), f16::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f16::NAN).to_bits()); + assert_eq!(::SIG_BITS, 10); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -22); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 5); + assert_eq!(::MIN_EXPONENT_FAST_PATH, -4); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 4); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 7); + assert_eq!(::EXP_MIN, -14); + assert_eq!(::EXP_SAT, 0x1f); + assert_eq!(::SMALLEST_POWER_OF_TEN, -27); + assert_eq!(::LARGEST_POWER_OF_TEN, 4); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 2048); +} + #[test] fn test_f32_consts() { assert_eq!(::INFINITY, f32::INFINITY); diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index 0db80fbd5250..6d49d85170e2 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -1,6 +1,12 @@ use core::num::dec2flt::float::RawFloat; use core::num::dec2flt::lemire::compute_float; +#[cfg(target_has_reliable_f16)] +fn compute_float16(q: i64, w: u64) -> (i32, u64) { + let fp = compute_float::(q, w); + (fp.p_biased, fp.m) +} + fn compute_float32(q: i64, w: u64) -> (i32, u64) { let fp = compute_float::(q, w); (fp.p_biased, fp.m) @@ -11,23 +17,73 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) { (fp.p_biased, fp.m) } +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn compute_float_f16_rounding() { + // The maximum integer that cna be converted to a `f16` without lost precision. + let val = 1 << 11; + let scale = 10_u64.pow(10); + + // These test near-halfway cases for half-precision floats. + assert_eq!(compute_float16(0, val), (26, 0)); + assert_eq!(compute_float16(0, val + 1), (26, 0)); + assert_eq!(compute_float16(0, val + 2), (26, 1)); + assert_eq!(compute_float16(0, val + 3), (26, 2)); + assert_eq!(compute_float16(0, val + 4), (26, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 12; + assert_eq!(compute_float16(0, val2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 4), (27, 1)); + assert_eq!(compute_float16(0, val2 + 6), (27, 2)); + assert_eq!(compute_float16(0, val2 + 8), (27, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float16(-10, val * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 1) * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 2) * scale), (26, 1)); + // Let's check the lines to see if anything is different in table... + assert_eq!(compute_float16(-10, (val + 3) * scale), (26, 2)); + assert_eq!(compute_float16(-10, (val + 4) * scale), (26, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float16(4, 6), (f16::INFINITE_POWER - 1, 851)); + assert_eq!(compute_float16(4, 7), (f16::INFINITE_POWER, 0)); // infinity + assert_eq!(compute_float16(2, 655), (f16::INFINITE_POWER - 1, 1023)); +} + #[test] fn compute_float_f32_rounding() { - // These test near-halfway cases for single-precision floats. - assert_eq!(compute_float32(0, 16777216), (151, 0)); - assert_eq!(compute_float32(0, 16777217), (151, 0)); - assert_eq!(compute_float32(0, 16777218), (151, 1)); - assert_eq!(compute_float32(0, 16777219), (151, 2)); - assert_eq!(compute_float32(0, 16777220), (151, 2)); + // the maximum integer that cna be converted to a `f32` without lost precision. + let val = 1 << 24; + let scale = 10_u64.pow(10); - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float32(-10, 167772160000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772170000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772180000000000), (151, 1)); + // These test near-halfway cases for single-precision floats. + assert_eq!(compute_float32(0, val), (151, 0)); + assert_eq!(compute_float32(0, val + 1), (151, 0)); + assert_eq!(compute_float32(0, val + 2), (151, 1)); + assert_eq!(compute_float32(0, val + 3), (151, 2)); + assert_eq!(compute_float32(0, val + 4), (151, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 25; + assert_eq!(compute_float32(0, val2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 4), (152, 1)); + assert_eq!(compute_float32(0, val2 + 6), (152, 2)); + assert_eq!(compute_float32(0, val2 + 8), (152, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float32(-10, val * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 1) * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 2) * scale), (151, 1)); // Let's check the lines to see if anything is different in table... - assert_eq!(compute_float32(-10, 167772190000000000), (151, 2)); - assert_eq!(compute_float32(-10, 167772200000000000), (151, 2)); + assert_eq!(compute_float32(-10, (val + 3) * scale), (151, 2)); + assert_eq!(compute_float32(-10, (val + 4) * scale), (151, 2)); // Check the rounding point between infinity and the next representable number down assert_eq!(compute_float32(38, 3), (f32::INFINITE_POWER - 1, 6402534)); @@ -37,23 +93,38 @@ fn compute_float_f32_rounding() { #[test] fn compute_float_f64_rounding() { - // These test near-halfway cases for double-precision floats. - assert_eq!(compute_float64(0, 9007199254740992), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740993), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740994), (1076, 1)); - assert_eq!(compute_float64(0, 9007199254740995), (1076, 2)); - assert_eq!(compute_float64(0, 9007199254740996), (1076, 2)); - assert_eq!(compute_float64(0, 18014398509481984), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481986), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481988), (1077, 1)); - assert_eq!(compute_float64(0, 18014398509481990), (1077, 2)); - assert_eq!(compute_float64(0, 18014398509481992), (1077, 2)); + // The maximum integer that cna be converted to a `f64` without lost precision. + let val = 1 << 53; + let scale = 1000; - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1)); - assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2)); - assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2)); + // These test near-halfway cases for double-precision floats. + assert_eq!(compute_float64(0, val), (1076, 0)); + assert_eq!(compute_float64(0, val + 1), (1076, 0)); + assert_eq!(compute_float64(0, val + 2), (1076, 1)); + assert_eq!(compute_float64(0, val + 3), (1076, 2)); + assert_eq!(compute_float64(0, val + 4), (1076, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 54; + assert_eq!(compute_float64(0, val2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 4), (1077, 1)); + assert_eq!(compute_float64(0, val2 + 6), (1077, 2)); + assert_eq!(compute_float64(0, val2 + 8), (1077, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float64(-3, val * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 1) * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 2) * scale), (1076, 1)); + assert_eq!(compute_float64(-3, (val + 3) * scale), (1076, 2)); + assert_eq!(compute_float64(-3, (val + 4) * scale), (1076, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float64(308, 1), (f64::INFINITE_POWER - 1, 506821272651936)); + assert_eq!(compute_float64(308, 2), (f64::INFINITE_POWER, 0)); // infinity + assert_eq!( + compute_float64(292, 17976931348623157), + (f64::INFINITE_POWER - 1, 4503599627370495) + ); } diff --git a/library/coretests/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs index a9025be5ca7f..b8ca220847cf 100644 --- a/library/coretests/tests/num/dec2flt/mod.rs +++ b/library/coretests/tests/num/dec2flt/mod.rs @@ -11,15 +11,23 @@ mod parse; // Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32. macro_rules! test_literal { ($x: expr) => {{ + #[cfg(target_has_reliable_f16)] + let x16: f16 = $x; let x32: f32 = $x; let x64: f64 = $x; let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)]; + for input in inputs { - assert_eq!(input.parse(), Ok(x64)); - assert_eq!(input.parse(), Ok(x32)); + assert_eq!(input.parse(), Ok(x64), "failed f64 {input}"); + assert_eq!(input.parse(), Ok(x32), "failed f32 {input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(input.parse(), Ok(x16), "failed f16 {input}"); + let neg_input = format!("-{input}"); - assert_eq!(neg_input.parse(), Ok(-x64)); - assert_eq!(neg_input.parse(), Ok(-x32)); + assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}"); + assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}"); } }}; } @@ -84,48 +92,87 @@ fn fast_path_correct() { test_literal!(1.448997445238699); } +// FIXME(f16_f128): remove gates once tests work on all targets + #[test] fn lonely_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); } #[test] fn exponentiated_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); } #[test] fn lonely_sign() { - assert!("+".parse::().is_err()); - assert!("-".parse::().is_err()); + #[cfg(target_has_reliable_f16)] + assert!("+".parse::().is_err()); + assert!("-".parse::().is_err()); + assert!("+".parse::().is_err()); } #[test] fn whitespace() { + #[cfg(target_has_reliable_f16)] + assert!("1.0 ".parse::().is_err()); assert!(" 1.0".parse::().is_err()); assert!("1.0 ".parse::().is_err()); } #[test] fn nan() { + #[cfg(target_has_reliable_f16)] + { + 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()); + assert!("-NaN".parse::().unwrap().is_nan()); } #[test] fn inf() { - assert_eq!("inf".parse(), Ok(f64::INFINITY)); - assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); + #[cfg(target_has_reliable_f16)] + { + assert_eq!("inf".parse(), Ok(f16::INFINITY)); + assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY)); + } + assert_eq!("inf".parse(), Ok(f32::INFINITY)); assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY)); + + assert_eq!("inf".parse(), Ok(f64::INFINITY)); + assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); } #[test] fn massive_exponent() { + #[cfg(target_has_reliable_f16)] + { + let max = i16::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16)); + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + } + + let max = i32::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32)); + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + let max = i64::MAX; assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); - assert_eq!(format!("1e-{max}000").parse(), Ok(0.0)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64)); assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); } diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index 59be3915052d..dccb6b5528d4 100644 --- a/library/coretests/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs @@ -10,6 +10,9 @@ fn new_dec(e: i64, m: u64) -> Decimal { fn missing_pieces() { let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"]; for &s in permutations { + #[cfg(target_has_reliable_f16)] + assert_eq!(dec2flt::(s), Err(pfe_invalid())); + assert_eq!(dec2flt::(s), Err(pfe_invalid())); assert_eq!(dec2flt::(s), Err(pfe_invalid())); } } @@ -17,15 +20,31 @@ fn missing_pieces() { #[test] fn invalid_chars() { let invalid = "r,?(&input) == error, "did not reject invalid {:?}", input); + + #[cfg(target_has_reliable_f16)] + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f16 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f32 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f64 did not reject invalid {input:?}", + ); } } } diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs index c64bb0a30720..ce36db33d05f 100644 --- a/library/coretests/tests/num/flt2dec/mod.rs +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -16,7 +16,7 @@ mod random; pub fn decode_finite(v: T) -> Decoded { match decode(v).1 { FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {full_decoded:?} instead"), + full_decoded => panic!("expected finite, got {full_decoded:?} instead for {v:?}"), } } @@ -75,6 +75,11 @@ macro_rules! try_fixed { }) } +#[cfg(target_has_reliable_f16)] +fn ldexp_f16(a: f16, b: i32) -> f16 { + ldexp_f64(a as f64, b) as f16 +} + fn ldexp_f32(a: f32, b: i32) -> f32 { ldexp_f64(a as f64, b) as f32 } @@ -176,6 +181,13 @@ trait TestableFloat: DecodableFloat + fmt::Display { fn ldexpi(f: i64, exp: isize) -> Self; } +#[cfg(target_has_reliable_f16)] +impl TestableFloat for f16 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + impl TestableFloat for f32 { fn ldexpi(f: i64, exp: isize) -> Self { f as Self * (exp as Self).exp2() @@ -225,6 +237,76 @@ macro_rules! check_exact_one { // // [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion // ftp://ftp.ee.lbl.gov/testbase-report.ps.Z +// or https://www.icir.org/vern/papers/testbase-report.pdf + +#[cfg(target_has_reliable_f16)] +pub fn f16_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999145507813 + // 0.0999755859375 + // 0.100036621094 + check_shortest!(f(0.1f16) => b"1", 0); + + // 0.3330078125 + // 0.333251953125 (1/3 in the default rounding) + // 0.33349609375 + check_shortest!(f(1.0f16/3.0) => b"3333", 0); + + // 10^1 * 0.3138671875 + // 10^1 * 0.3140625 + // 10^1 * 0.3142578125 + check_shortest!(f(3.14f16) => b"314", 1); + + // 10^18 * 0.31415916243714048 + // 10^18 * 0.314159196796878848 + // 10^18 * 0.314159231156617216 + check_shortest!(f(3.1415e4f16) => b"3141", 5); + + // regression test for decoders + // 10^2 * 0.31984375 + // 10^2 * 0.32 + // 10^2 * 0.3203125 + check_shortest!(f(ldexp_f16(1.0, 5)) => b"32", 2); + + // 10^5 * 0.65472 + // 10^5 * 0.65504 + // 10^5 * 0.65536 + check_shortest!(f(f16::MAX) => b"655", 5); + + // 10^-4 * 0.60975551605224609375 + // 10^-4 * 0.6103515625 + // 10^-4 * 0.61094760894775390625 + check_shortest!(f(f16::MIN_POSITIVE) => b"6104", -4); + + // 10^-9 * 0 + // 10^-9 * 0.59604644775390625 + // 10^-8 * 0.11920928955078125 + let minf16 = ldexp_f16(1.0, -24); + check_shortest!(f(minf16) => b"6", -7); +} + +#[cfg(target_has_reliable_f16)] +pub fn f16_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf16 = ldexp_f16(1.0, -24); + + check_exact!(f(0.1f16) => b"999755859375 ", -1); + check_exact!(f(0.5f16) => b"5 ", 0); + check_exact!(f(1.0f16/3.0) => b"333251953125 ", 0); + check_exact!(f(3.141f16) => b"3140625 ", 1); + check_exact!(f(3.141e4f16) => b"31408 ", 5); + check_exact!(f(f16::MAX) => b"65504 ", 5); + check_exact!(f(f16::MIN_POSITIVE) => b"6103515625 ", -4); + check_exact!(f(minf16) => b"59604644775390625", -7); + + // FIXME(f16_f128): these should gain the check_exact_one tests like `f32` and `f64` have, + // but these values are not easy to generate. The algorithm from the Paxon paper [1] needs + // to be adapted to binary16. +} pub fn f32_shortest_sanity_test(mut f: F) where @@ -553,23 +635,45 @@ where assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); - assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65500"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65500.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 8), "65500.00000000"); - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 9), "0.000000060"); + } - assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); + { + // f32 + assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + } + + { + // f64 + assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); + assert_eq!( + to_string(f, f64::MAX, Minus, 8), + format!("17976931348623157{:0>292}.00000000", "") + ); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + } if cfg!(miri) { // Miri is too slow @@ -655,27 +759,45 @@ where assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); - assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, (-2, 2), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-4, 4), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-5, 5), false), "65500"); - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, (-2, 2), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-7, 7), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-8, 8), false), "0.00000006"); + } - assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); - assert_eq!( - to_string(f, f64::MAX, Minus, (-308, 309), false), - format!("17976931348623157{:0>292}", "") - ); - assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + { + // f32 + assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); - assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + } + { + // f64 + assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); + assert_eq!( + to_string(f, f64::MAX, Minus, (-308, 309), false), + format!("17976931348623157{:0>292}", "") + ); + assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + } assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); } @@ -791,6 +913,26 @@ where "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 1, false), "7e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 2, false), "6.6e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 4, false), "6.550e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 5, false), "6.5504e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 6, false), "6.55040e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 16, false), "6.550400000000000e4"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 1, false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, 2, false), "6.0e-8"); + assert_eq!(to_string(f, minf16, Minus, 4, false), "5.960e-8"); + assert_eq!(to_string(f, minf16, Minus, 8, false), "5.9604645e-8"); + assert_eq!(to_string(f, minf16, Minus, 16, false), "5.960464477539062e-8"); + assert_eq!(to_string(f, minf16, Minus, 17, false), "5.9604644775390625e-8"); + assert_eq!(to_string(f, minf16, Minus, 18, false), "5.96046447753906250e-8"); + assert_eq!(to_string(f, minf16, Minus, 24, false), "5.96046447753906250000000e-8"); + } + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); @@ -1069,6 +1211,13 @@ where "0.000000999999999999999954748111825886258685613938723690807819366455078125000" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65504"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65504.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 2), "65504.00"); + } + assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); @@ -1078,6 +1227,21 @@ where return; } + #[cfg(target_has_reliable_f16)] + { + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0"); + assert_eq!(to_string(f, minf16, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf16, Minus, 2), "0.00"); + assert_eq!(to_string(f, minf16, Minus, 4), "0.0000"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 10), "0.0000000596"); + assert_eq!(to_string(f, minf16, Minus, 15), "0.000000059604645"); + assert_eq!(to_string(f, minf16, Minus, 20), "0.00000005960464477539"); + assert_eq!(to_string(f, minf16, Minus, 24), "0.000000059604644775390625"); + assert_eq!(to_string(f, minf16, Minus, 32), "0.00000005960464477539062500000000"); + } + let minf32 = ldexp_f32(1.0, -149); assert_eq!(to_string(f, minf32, Minus, 0), "0"); assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); diff --git a/library/coretests/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs index 586b49df7d9b..7386139aaced 100644 --- a/library/coretests/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -79,6 +79,20 @@ where (npassed, nignored) } +#[cfg(target_has_reliable_f16)] +pub fn f16_random_equivalence_test(f: F, g: G, k: usize, n: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + let mut rng = crate::test_rng(); + let f16_range = Uniform::new(0x0001u16, 0x7c00).unwrap(); + iterate("f16_random_equivalence_test", k, n, f, g, |_| { + let x = f16::from_bits(f16_range.sample(&mut rng)); + decode_finite(x) + }); +} + pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -105,6 +119,24 @@ where }); } +#[cfg(target_has_reliable_f16)] +pub fn f16_exhaustive_equivalence_test(f: F, g: G, k: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // Unlike the other float types, `f16` is small enough that these exhaustive tests + // can run in less than a second so we don't need to ignore it. + + // iterate from 0x0001 to 0x7bff, i.e., all finite ranges + let (npassed, nignored) = + iterate("f16_exhaustive_equivalence_test", k, 0x7bff, f, g, |i: usize| { + let x = f16::from_bits(i as u16 + 1); + decode_finite(x) + }); + assert_eq!((npassed, nignored), (29735, 2008)); +} + pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -133,6 +165,17 @@ fn shortest_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); + #[cfg(target_has_reliable_f16)] + f16_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is to slow +#[cfg(target_has_reliable_f16)] +fn shortest_f16_exhaustive_equivalence_test() { + // see the f32 version + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f16_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); } #[test] @@ -158,6 +201,23 @@ fn shortest_f64_hard_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000); } +#[test] +#[cfg(target_has_reliable_f16)] +fn exact_f16_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + // Miri is too slow + let n = if cfg!(miri) { 3 } else { 1_000 }; + + for k in 1..21 { + f16_random_equivalence_test( + |d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), + k, + n, + ); + } +} + #[test] fn exact_f32_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_exact as fallback; diff --git a/library/coretests/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs index be25fee3f6c7..43bb6024f9ce 100644 --- a/library/coretests/tests/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/tests/num/flt2dec/strategy/dragon.rs @@ -18,6 +18,8 @@ fn test_mul_pow10() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -41,6 +43,9 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] diff --git a/library/coretests/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs index 9b2f0453de73..117191e0c8fd 100644 --- a/library/coretests/tests/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/tests/num/flt2dec/strategy/grisu.rs @@ -38,6 +38,8 @@ fn test_max_pow10_no_more_than() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -50,6 +52,8 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 0add9a01e682..a6b75f702660 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -732,157 +732,157 @@ assume_usize_width! { } macro_rules! test_float { - ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { + ($modname: ident, $fassert: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { mod $modname { #[test] fn min() { - assert_eq!((0.0 as $fty).min(0.0), 0.0); - assert!((0.0 as $fty).min(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).min(-0.0), -0.0); - assert!((-0.0 as $fty).min(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).min(9.0), 9.0); - assert_eq!((-9.0 as $fty).min(0.0), -9.0); - assert_eq!((0.0 as $fty).min(9.0), 0.0); - assert!((0.0 as $fty).min(9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).min(9.0), -0.0); - assert!((-0.0 as $fty).min(9.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).min(-9.0), -9.0); - assert_eq!(($inf as $fty).min(9.0), 9.0); - assert_eq!((9.0 as $fty).min($inf), 9.0); - assert_eq!(($inf as $fty).min(-9.0), -9.0); - assert_eq!((-9.0 as $fty).min($inf), -9.0); - assert_eq!(($neginf as $fty).min(9.0), $neginf); - assert_eq!((9.0 as $fty).min($neginf), $neginf); - assert_eq!(($neginf as $fty).min(-9.0), $neginf); - assert_eq!((-9.0 as $fty).min($neginf), $neginf); - assert_eq!(($nan as $fty).min(9.0), 9.0); - assert_eq!(($nan as $fty).min(-9.0), -9.0); - assert_eq!((9.0 as $fty).min($nan), 9.0); - assert_eq!((-9.0 as $fty).min($nan), -9.0); - assert!(($nan as $fty).min($nan).is_nan()); + $fassert!((0.0 as $fty).min(0.0), 0.0); + $fassert!((0.0 as $fty).min(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).min(-0.0), -0.0); + $fassert!((-0.0 as $fty).min(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).min(9.0), 9.0); + $fassert!((-9.0 as $fty).min(0.0), -9.0); + $fassert!((0.0 as $fty).min(9.0), 0.0); + $fassert!((0.0 as $fty).min(9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).min(9.0), -0.0); + $fassert!((-0.0 as $fty).min(9.0).is_sign_negative()); + $fassert!((-0.0 as $fty).min(-9.0), -9.0); + $fassert!(($inf as $fty).min(9.0), 9.0); + $fassert!((9.0 as $fty).min($inf), 9.0); + $fassert!(($inf as $fty).min(-9.0), -9.0); + $fassert!((-9.0 as $fty).min($inf), -9.0); + $fassert!(($neginf as $fty).min(9.0), $neginf); + $fassert!((9.0 as $fty).min($neginf), $neginf); + $fassert!(($neginf as $fty).min(-9.0), $neginf); + $fassert!((-9.0 as $fty).min($neginf), $neginf); + $fassert!(($nan as $fty).min(9.0), 9.0); + $fassert!(($nan as $fty).min(-9.0), -9.0); + $fassert!((9.0 as $fty).min($nan), 9.0); + $fassert!((-9.0 as $fty).min($nan), -9.0); + $fassert!(($nan as $fty).min($nan).is_nan()); } #[test] fn max() { - assert_eq!((0.0 as $fty).max(0.0), 0.0); - assert!((0.0 as $fty).max(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).max(-0.0), -0.0); - assert!((-0.0 as $fty).max(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).max(9.0), 9.0); - assert_eq!((-9.0 as $fty).max(0.0), 0.0); - assert!((-9.0 as $fty).max(0.0).is_sign_positive()); - assert_eq!((-9.0 as $fty).max(-0.0), -0.0); - assert!((-9.0 as $fty).max(-0.0).is_sign_negative()); - assert_eq!((0.0 as $fty).max(9.0), 9.0); - assert_eq!((0.0 as $fty).max(-9.0), 0.0); - assert!((0.0 as $fty).max(-9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).max(-9.0), -0.0); - assert!((-0.0 as $fty).max(-9.0).is_sign_negative()); - assert_eq!(($inf as $fty).max(9.0), $inf); - assert_eq!((9.0 as $fty).max($inf), $inf); - assert_eq!(($inf as $fty).max(-9.0), $inf); - assert_eq!((-9.0 as $fty).max($inf), $inf); - assert_eq!(($neginf as $fty).max(9.0), 9.0); - assert_eq!((9.0 as $fty).max($neginf), 9.0); - assert_eq!(($neginf as $fty).max(-9.0), -9.0); - assert_eq!((-9.0 as $fty).max($neginf), -9.0); - assert_eq!(($nan as $fty).max(9.0), 9.0); - assert_eq!(($nan as $fty).max(-9.0), -9.0); - assert_eq!((9.0 as $fty).max($nan), 9.0); - assert_eq!((-9.0 as $fty).max($nan), -9.0); - assert!(($nan as $fty).max($nan).is_nan()); + $fassert!((0.0 as $fty).max(0.0), 0.0); + $fassert!((0.0 as $fty).max(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).max(-0.0), -0.0); + $fassert!((-0.0 as $fty).max(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).max(9.0), 9.0); + $fassert!((-9.0 as $fty).max(0.0), 0.0); + $fassert!((-9.0 as $fty).max(0.0).is_sign_positive()); + $fassert!((-9.0 as $fty).max(-0.0), -0.0); + $fassert!((-9.0 as $fty).max(-0.0).is_sign_negative()); + $fassert!((0.0 as $fty).max(9.0), 9.0); + $fassert!((0.0 as $fty).max(-9.0), 0.0); + $fassert!((0.0 as $fty).max(-9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).max(-9.0), -0.0); + $fassert!((-0.0 as $fty).max(-9.0).is_sign_negative()); + $fassert!(($inf as $fty).max(9.0), $inf); + $fassert!((9.0 as $fty).max($inf), $inf); + $fassert!(($inf as $fty).max(-9.0), $inf); + $fassert!((-9.0 as $fty).max($inf), $inf); + $fassert!(($neginf as $fty).max(9.0), 9.0); + $fassert!((9.0 as $fty).max($neginf), 9.0); + $fassert!(($neginf as $fty).max(-9.0), -9.0); + $fassert!((-9.0 as $fty).max($neginf), -9.0); + $fassert!(($nan as $fty).max(9.0), 9.0); + $fassert!(($nan as $fty).max(-9.0), -9.0); + $fassert!((9.0 as $fty).max($nan), 9.0); + $fassert!((-9.0 as $fty).max($nan), -9.0); + $fassert!(($nan as $fty).max($nan).is_nan()); } #[test] fn minimum() { - assert_eq!((0.0 as $fty).minimum(0.0), 0.0); - assert!((0.0 as $fty).minimum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).minimum(0.0), -0.0); - assert!((-0.0 as $fty).minimum(0.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).minimum(-0.0), -0.0); - assert!((-0.0 as $fty).minimum(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).minimum(9.0), 9.0); - assert_eq!((-9.0 as $fty).minimum(0.0), -9.0); - assert_eq!((0.0 as $fty).minimum(9.0), 0.0); - assert!((0.0 as $fty).minimum(9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).minimum(9.0), -0.0); - assert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).minimum(-9.0), -9.0); - assert_eq!(($inf as $fty).minimum(9.0), 9.0); - assert_eq!((9.0 as $fty).minimum($inf), 9.0); - assert_eq!(($inf as $fty).minimum(-9.0), -9.0); - assert_eq!((-9.0 as $fty).minimum($inf), -9.0); - assert_eq!(($neginf as $fty).minimum(9.0), $neginf); - assert_eq!((9.0 as $fty).minimum($neginf), $neginf); - assert_eq!(($neginf as $fty).minimum(-9.0), $neginf); - assert_eq!((-9.0 as $fty).minimum($neginf), $neginf); - assert!(($nan as $fty).minimum(9.0).is_nan()); - assert!(($nan as $fty).minimum(-9.0).is_nan()); - assert!((9.0 as $fty).minimum($nan).is_nan()); - assert!((-9.0 as $fty).minimum($nan).is_nan()); - assert!(($nan as $fty).minimum($nan).is_nan()); + $fassert!((0.0 as $fty).minimum(0.0), 0.0); + $fassert!((0.0 as $fty).minimum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).minimum(0.0), -0.0); + $fassert!((-0.0 as $fty).minimum(0.0).is_sign_negative()); + $fassert!((-0.0 as $fty).minimum(-0.0), -0.0); + $fassert!((-0.0 as $fty).minimum(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).minimum(9.0), 9.0); + $fassert!((-9.0 as $fty).minimum(0.0), -9.0); + $fassert!((0.0 as $fty).minimum(9.0), 0.0); + $fassert!((0.0 as $fty).minimum(9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).minimum(9.0), -0.0); + $fassert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); + $fassert!((-0.0 as $fty).minimum(-9.0), -9.0); + $fassert!(($inf as $fty).minimum(9.0), 9.0); + $fassert!((9.0 as $fty).minimum($inf), 9.0); + $fassert!(($inf as $fty).minimum(-9.0), -9.0); + $fassert!((-9.0 as $fty).minimum($inf), -9.0); + $fassert!(($neginf as $fty).minimum(9.0), $neginf); + $fassert!((9.0 as $fty).minimum($neginf), $neginf); + $fassert!(($neginf as $fty).minimum(-9.0), $neginf); + $fassert!((-9.0 as $fty).minimum($neginf), $neginf); + $fassert!(($nan as $fty).minimum(9.0).is_nan()); + $fassert!(($nan as $fty).minimum(-9.0).is_nan()); + $fassert!((9.0 as $fty).minimum($nan).is_nan()); + $fassert!((-9.0 as $fty).minimum($nan).is_nan()); + $fassert!(($nan as $fty).minimum($nan).is_nan()); } #[test] fn maximum() { - assert_eq!((0.0 as $fty).maximum(0.0), 0.0); - assert!((0.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(0.0), 0.0); - assert!((-0.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(-0.0), -0.0); - assert!((-0.0 as $fty).maximum(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).maximum(9.0), 9.0); - assert_eq!((-9.0 as $fty).maximum(0.0), 0.0); - assert!((-9.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-9.0 as $fty).maximum(-0.0), -0.0); - assert!((-9.0 as $fty).maximum(-0.0).is_sign_negative()); - assert_eq!((0.0 as $fty).maximum(9.0), 9.0); - assert_eq!((0.0 as $fty).maximum(-9.0), 0.0); - assert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(-9.0), -0.0); - assert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); - assert_eq!(($inf as $fty).maximum(9.0), $inf); - assert_eq!((9.0 as $fty).maximum($inf), $inf); - assert_eq!(($inf as $fty).maximum(-9.0), $inf); - assert_eq!((-9.0 as $fty).maximum($inf), $inf); - assert_eq!(($neginf as $fty).maximum(9.0), 9.0); - assert_eq!((9.0 as $fty).maximum($neginf), 9.0); - assert_eq!(($neginf as $fty).maximum(-9.0), -9.0); - assert_eq!((-9.0 as $fty).maximum($neginf), -9.0); - assert!(($nan as $fty).maximum(9.0).is_nan()); - assert!(($nan as $fty).maximum(-9.0).is_nan()); - assert!((9.0 as $fty).maximum($nan).is_nan()); - assert!((-9.0 as $fty).maximum($nan).is_nan()); - assert!(($nan as $fty).maximum($nan).is_nan()); + $fassert!((0.0 as $fty).maximum(0.0), 0.0); + $fassert!((0.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(0.0), 0.0); + $fassert!((-0.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(-0.0), -0.0); + $fassert!((-0.0 as $fty).maximum(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).maximum(9.0), 9.0); + $fassert!((-9.0 as $fty).maximum(0.0), 0.0); + $fassert!((-9.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-9.0 as $fty).maximum(-0.0), -0.0); + $fassert!((-9.0 as $fty).maximum(-0.0).is_sign_negative()); + $fassert!((0.0 as $fty).maximum(9.0), 9.0); + $fassert!((0.0 as $fty).maximum(-9.0), 0.0); + $fassert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(-9.0), -0.0); + $fassert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); + $fassert!(($inf as $fty).maximum(9.0), $inf); + $fassert!((9.0 as $fty).maximum($inf), $inf); + $fassert!(($inf as $fty).maximum(-9.0), $inf); + $fassert!((-9.0 as $fty).maximum($inf), $inf); + $fassert!(($neginf as $fty).maximum(9.0), 9.0); + $fassert!((9.0 as $fty).maximum($neginf), 9.0); + $fassert!(($neginf as $fty).maximum(-9.0), -9.0); + $fassert!((-9.0 as $fty).maximum($neginf), -9.0); + $fassert!(($nan as $fty).maximum(9.0).is_nan()); + $fassert!(($nan as $fty).maximum(-9.0).is_nan()); + $fassert!((9.0 as $fty).maximum($nan).is_nan()); + $fassert!((-9.0 as $fty).maximum($nan).is_nan()); + $fassert!(($nan as $fty).maximum($nan).is_nan()); } #[test] fn midpoint() { - assert_eq!((0.5 as $fty).midpoint(0.5), 0.5); - assert_eq!((0.5 as $fty).midpoint(2.5), 1.5); - assert_eq!((3.0 as $fty).midpoint(4.0), 3.5); - assert_eq!((-3.0 as $fty).midpoint(4.0), 0.5); - assert_eq!((3.0 as $fty).midpoint(-4.0), -0.5); - assert_eq!((-3.0 as $fty).midpoint(-4.0), -3.5); - assert_eq!((0.0 as $fty).midpoint(0.0), 0.0); - assert_eq!((-0.0 as $fty).midpoint(-0.0), -0.0); - assert_eq!((-5.0 as $fty).midpoint(5.0), 0.0); - assert_eq!(($max as $fty).midpoint($min), 0.0); - assert_eq!(($min as $fty).midpoint($max), -0.0); - assert_eq!(($max as $fty).midpoint($min_pos), $max / 2.); - assert_eq!((-$max as $fty).midpoint($min_pos), -$max / 2.); - assert_eq!(($max as $fty).midpoint(-$min_pos), $max / 2.); - assert_eq!((-$max as $fty).midpoint(-$min_pos), -$max / 2.); - assert_eq!(($min_pos as $fty).midpoint($max), $max / 2.); - assert_eq!(($min_pos as $fty).midpoint(-$max), -$max / 2.); - assert_eq!((-$min_pos as $fty).midpoint($max), $max / 2.); - assert_eq!((-$min_pos as $fty).midpoint(-$max), -$max / 2.); - assert_eq!(($max as $fty).midpoint($max), $max); - assert_eq!(($min_pos as $fty).midpoint($min_pos), $min_pos); - assert_eq!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos); - assert_eq!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5); - assert_eq!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5); - assert_eq!(($inf as $fty).midpoint($inf), $inf); - assert_eq!(($neginf as $fty).midpoint($neginf), $neginf); - assert!(($nan as $fty).midpoint(1.0).is_nan()); - assert!((1.0 as $fty).midpoint($nan).is_nan()); - assert!(($nan as $fty).midpoint($nan).is_nan()); + $fassert!((0.5 as $fty).midpoint(0.5), 0.5); + $fassert!((0.5 as $fty).midpoint(2.5), 1.5); + $fassert!((3.0 as $fty).midpoint(4.0), 3.5); + $fassert!((-3.0 as $fty).midpoint(4.0), 0.5); + $fassert!((3.0 as $fty).midpoint(-4.0), -0.5); + $fassert!((-3.0 as $fty).midpoint(-4.0), -3.5); + $fassert!((0.0 as $fty).midpoint(0.0), 0.0); + $fassert!((-0.0 as $fty).midpoint(-0.0), -0.0); + $fassert!((-5.0 as $fty).midpoint(5.0), 0.0); + $fassert!(($max as $fty).midpoint($min), 0.0); + $fassert!(($min as $fty).midpoint($max), -0.0); + $fassert!(($max as $fty).midpoint($min_pos), $max / 2.); + $fassert!((-$max as $fty).midpoint($min_pos), -$max / 2.); + $fassert!(($max as $fty).midpoint(-$min_pos), $max / 2.); + $fassert!((-$max as $fty).midpoint(-$min_pos), -$max / 2.); + $fassert!(($min_pos as $fty).midpoint($max), $max / 2.); + $fassert!(($min_pos as $fty).midpoint(-$max), -$max / 2.); + $fassert!((-$min_pos as $fty).midpoint($max), $max / 2.); + $fassert!((-$min_pos as $fty).midpoint(-$max), -$max / 2.); + $fassert!(($max as $fty).midpoint($max), $max); + $fassert!(($min_pos as $fty).midpoint($min_pos), $min_pos); + $fassert!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos); + $fassert!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5); + $fassert!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5); + $fassert!(($inf as $fty).midpoint($inf), $inf); + $fassert!(($neginf as $fty).midpoint($neginf), $neginf); + $fassert!(($nan as $fty).midpoint(1.0).is_nan()); + $fassert!((1.0 as $fty).midpoint($nan).is_nan()); + $fassert!(($nan as $fty).midpoint($nan).is_nan()); // test if large differences in magnitude are still correctly computed. // NOTE: that because of how small x and y are, x + y can never overflow @@ -907,19 +907,19 @@ macro_rules! test_float { } #[test] fn rem_euclid() { - let a: $fty = 42.0; - assert!($inf.rem_euclid(a).is_nan()); - assert_eq!(a.rem_euclid($inf), a); - assert!(a.rem_euclid($nan).is_nan()); + // FIXME: Use $fassert when rem_euclid becomes const + assert!($inf.rem_euclid((42.0 as $fty)).is_nan()); + assert_eq!((42.0 as $fty).rem_euclid($inf), (42.0 as $fty)); + assert!((42.0 as $fty).rem_euclid($nan).is_nan()); assert!($inf.rem_euclid($inf).is_nan()); assert!($inf.rem_euclid($nan).is_nan()); assert!($nan.rem_euclid($inf).is_nan()); } #[test] fn div_euclid() { - let a: $fty = 42.0; - assert_eq!(a.div_euclid($inf), 0.0); - assert!(a.div_euclid($nan).is_nan()); + // FIXME: Use $fassert when div_euclid becomes const + assert_eq!((42.0 as $fty).div_euclid($inf), 0.0); + assert!((42.0 as $fty).div_euclid($nan).is_nan()); assert!($inf.div_euclid($inf).is_nan()); assert!($inf.div_euclid($nan).is_nan()); assert!($nan.div_euclid($inf).is_nan()); @@ -928,8 +928,41 @@ macro_rules! test_float { }; } +// Custom assert macro that distribute between assert! and assert_eq! in a non-const context +macro_rules! float_assert { + ($b:expr) => { + assert!($b); + }; + ($left:expr, $right:expr) => { + assert_eq!($left, $right); + }; +} + +// Custom assert macro that only uses assert! in a const context +macro_rules! float_const_assert { + ($b:expr) => { + assert!(const { $b }); + }; + ($left:expr, $right:expr) => { + assert!(const { $left == $right }); + }; +} + test_float!( f32, + float_assert, + f32, + f32::INFINITY, + f32::NEG_INFINITY, + f32::NAN, + f32::MIN, + f32::MAX, + f32::MIN_POSITIVE, + f32::MAX_EXP +); +test_float!( + f32_const, + float_const_assert, f32, f32::INFINITY, f32::NEG_INFINITY, @@ -941,6 +974,19 @@ test_float!( ); test_float!( f64, + float_assert, + f64, + f64::INFINITY, + f64::NEG_INFINITY, + f64::NAN, + f64::MIN, + f64::MAX, + f64::MIN_POSITIVE, + f64::MAX_EXP +); +test_float!( + f64_const, + float_const_assert, f64, f64::INFINITY, f64::NEG_INFINITY, diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index 6f43ac4809a3..d7d169671f01 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -12,10 +12,11 @@ bench = false doc = false [dependencies] -alloc = { path = "../alloc" } -cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } core = { path = "../core" } compiler_builtins = "0.1.0" -[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +[target.'cfg(target_os = "android")'.dependencies] libc = { version = "0.2", default-features = false } + +[target.'cfg(any(target_os = "android", target_os = "zkvm"))'.dependencies] +alloc = { path = "../alloc" } diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index b2ad0f4ac3d0..d1706b652529 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -7,15 +7,11 @@ #![unstable(feature = "panic_abort", issue = "32837")] #![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] #![panic_runtime] -#![allow(unused_features)] -#![feature(asm_experimental_arch)] -#![feature(core_intrinsics)] #![feature(panic_runtime)] #![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] #![allow(internal_features)] -#![deny(unsafe_op_in_unsafe_fn)] #[cfg(target_os = "android")] mod android; @@ -45,75 +41,13 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { zkvm::zkvm_set_abort_message(_payload); } - unsafe { - abort(); + unsafe extern "Rust" { + // This is defined in std::rt. + #[rustc_std_internal_symbol] + safe fn __rust_abort() -> !; } - cfg_if::cfg_if! { - if #[cfg(any(unix, target_os = "solid_asp3"))] { - unsafe fn abort() -> ! { - unsafe { libc::abort(); } - } - } else if #[cfg(any(target_os = "hermit", - all(target_vendor = "fortanix", target_env = "sgx"), - target_os = "xous", - target_os = "uefi", - ))] { - unsafe fn abort() -> ! { - // call std::sys::abort_internal - unsafe extern "C" { - pub fn __rust_abort() -> !; - } - unsafe { __rust_abort(); } - } - } else if #[cfg(all(windows, not(miri)))] { - // On Windows, use the processor-specific __fastfail mechanism. In Windows 8 - // and later, this will terminate the process immediately without running any - // in-process exception handlers. In earlier versions of Windows, this - // sequence of instructions will be treated as an access violation, - // terminating the process but without necessarily bypassing all exception - // handlers. - // - // https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail - // - // Note: this is the same implementation as in std's `abort_internal` - unsafe fn abort() -> ! { - #[allow(unused)] - const FAST_FAIL_FATAL_APP_EXIT: usize = 7; - cfg_if::cfg_if! { - if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - unsafe { - core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { - unsafe { - core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { - unsafe { - core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else { - core::intrinsics::abort(); - } - } - } - } else if #[cfg(target_os = "teeos")] { - mod teeos { - unsafe extern "C" { - pub fn TEE_Panic(code: u32) -> !; - } - } - - unsafe fn abort() -> ! { - unsafe { teeos::TEE_Panic(1); } - } - } else { - unsafe fn abort() -> ! { - core::intrinsics::abort(); - } - } - } + __rust_abort() } // This... is a bit of an oddity. The tl;dr; is that this is required to link diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs index 8f4562d07fc4..b36d1a019fdd 100644 --- a/library/panic_unwind/src/hermit.rs +++ b/library/panic_unwind/src/hermit.rs @@ -5,20 +5,16 @@ use alloc::boxed::Box; use core::any::Any; +unsafe extern "Rust" { + // This is defined in std::rt + #[rustc_std_internal_symbol] + safe fn __rust_abort() -> !; +} + pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { - unsafe extern "C" { - fn __rust_abort() -> !; - } - unsafe { - __rust_abort(); - } + __rust_abort() } pub(crate) unsafe fn panic(_data: Box) -> u32 { - unsafe extern "C" { - fn __rust_abort() -> !; - } - unsafe { - __rust_abort(); - } + __rust_abort() } diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 6b2ba2e714c9..bb4acde48224 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -14,365 +14,6 @@ use crate::sys::cmath; #[cfg(not(test))] impl f128 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.7_f128; - /// let g = 3.0_f128; - /// let h = -3.7_f128; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f128 { - unsafe { intrinsics::floorf128(self) } - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.01_f128; - /// let g = 4.0_f128; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f128 { - unsafe { intrinsics::ceilf128(self) } - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.3_f128; - /// let g = -3.3_f128; - /// let h = -3.7_f128; - /// let i = 3.5_f128; - /// let j = 4.5_f128; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f128 { - unsafe { intrinsics::roundf128(self) } - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.3_f128; - /// let g = -3.3_f128; - /// let h = 3.5_f128; - /// let i = 4.5_f128; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f128 { - intrinsics::round_ties_even_f128(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.7_f128; - /// let g = 3.0_f128; - /// let h = -3.7_f128; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f128 { - unsafe { intrinsics::truncf128(self) } - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 3.6_f128; - /// let y = -3.6_f128; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f128::EPSILON); - /// assert!(abs_difference_y <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f128 { - self - self.trunc() - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let m = 10.0_f128; - /// let x = 4.0_f128; - /// let b = 60.0_f128; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f128 + f128::EPSILON; - /// let one_minus_eps = 1.0_f128 - f128::EPSILON; - /// let minus_one = -1.0_f128; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f128, b: f128) -> f128 { - unsafe { intrinsics::fmaf128(self, a, b) } - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let a: f128 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn div_euclid(self, rhs: f128) -> f128 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q - } - - /// Calculates the least nonnegative remainder of `self (mod rhs)`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let a: f128 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "modulo", alias = "mod")] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn rem_euclid(self, rhs: f128) -> f128 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 2.0_f128; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= f128::EPSILON); - /// - /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } - } - /// Raises a number to a floating point power. /// /// # Unspecified precision @@ -405,43 +46,6 @@ impl f128 { unsafe { intrinsics::powf128(self, n) } } - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let positive = 4.0_f128; - /// let negative = -4.0_f128; - /// let negative_zero = -0.0_f128; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// # } - /// ``` - #[inline] - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sqrt(self) -> f128 { - unsafe { intrinsics::sqrtf128(self) } - } - /// Returns `e^(self)`, (the exponential function). /// /// # Unspecified precision diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index d6bc1d3118ae..4792eac1f9e2 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -14,365 +14,6 @@ use crate::sys::cmath; #[cfg(not(test))] impl f16 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.7_f16; - /// let g = 3.0_f16; - /// let h = -3.7_f16; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f16 { - unsafe { intrinsics::floorf16(self) } - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.01_f16; - /// let g = 4.0_f16; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f16 { - unsafe { intrinsics::ceilf16(self) } - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.3_f16; - /// let g = -3.3_f16; - /// let h = -3.7_f16; - /// let i = 3.5_f16; - /// let j = 4.5_f16; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f16 { - unsafe { intrinsics::roundf16(self) } - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.3_f16; - /// let g = -3.3_f16; - /// let h = 3.5_f16; - /// let i = 4.5_f16; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f16 { - intrinsics::round_ties_even_f16(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.7_f16; - /// let g = 3.0_f16; - /// let h = -3.7_f16; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f16 { - unsafe { intrinsics::truncf16(self) } - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 3.6_f16; - /// let y = -3.6_f16; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f16::EPSILON); - /// assert!(abs_difference_y <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f16 { - self - self.trunc() - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let m = 10.0_f16; - /// let x = 4.0_f16; - /// let b = 60.0_f16; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f16 + f16::EPSILON; - /// let one_minus_eps = 1.0_f16 - f16::EPSILON; - /// let minus_one = -1.0_f16; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f16, b: f16) -> f16 { - unsafe { intrinsics::fmaf16(self, a, b) } - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let a: f16 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn div_euclid(self, rhs: f16) -> f16 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q - } - - /// Calculates the least nonnegative remainder of `self (mod rhs)`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let a: f16 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "modulo", alias = "mod")] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn rem_euclid(self, rhs: f16) -> f16 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 2.0_f16; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= f16::EPSILON); - /// - /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } - } - /// Raises a number to a floating point power. /// /// # Unspecified precision @@ -405,43 +46,6 @@ impl f16 { unsafe { intrinsics::powf16(self, n) } } - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let positive = 4.0_f16; - /// let negative = -4.0_f16; - /// let negative_zero = -0.0_f16; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// # } - /// ``` - #[inline] - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sqrt(self) -> f16 { - unsafe { intrinsics::sqrtf16(self) } - } - /// Returns `e^(self)`, (the exponential function). /// /// # Unspecified precision @@ -702,41 +306,6 @@ impl f16 { unsafe { intrinsics::log10f16(self) } } - /// Returns the cube root of a number. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `cbrtf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 8.0f16; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cbrt(self) -> f16 { - cmath::cbrtf(self as f32) as f16 - } - /// Compute the distance between the origin and a point (`x`, `y`) on the /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a /// right-angle triangle with other sides having length `x.abs()` and diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index baf7002f3803..94140d01d8b7 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -46,7 +46,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn floor(self) -> f32 { - unsafe { intrinsics::floorf32(self) } + core::f32::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -68,7 +68,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ceil(self) -> f32 { - unsafe { intrinsics::ceilf32(self) } + core::f32::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -96,7 +96,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn round(self) -> f32 { - unsafe { intrinsics::roundf32(self) } + core::f32::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -122,7 +122,7 @@ impl f32 { #[stable(feature = "round_ties_even", since = "1.77.0")] #[inline] pub fn round_ties_even(self) -> f32 { - intrinsics::round_ties_even_f32(self) + core::f32::round_ties_even(self) } /// Returns the integer part of `self`. @@ -147,7 +147,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn trunc(self) -> f32 { - unsafe { intrinsics::truncf32(self) } + core::f32::trunc(self) } /// Returns the fractional part of `self`. @@ -170,7 +170,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn fract(self) -> f32 { - self - self.trunc() + core::f32::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +212,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f32, b: f32) -> f32 { - unsafe { intrinsics::fmaf32(self, a, b) } + core::f32::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,11 +242,7 @@ impl f32 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f32) -> f32 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q + core::f32::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -283,8 +279,7 @@ impl f32 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f32) -> f32 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } + core::f32::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -312,7 +307,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f32 { - unsafe { intrinsics::powif32(self, n) } + core::f32::powi(self, n) } /// Raises a number to a floating point power. @@ -367,7 +362,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f32 { - unsafe { intrinsics::sqrtf32(self) } + core::f32::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -599,7 +594,8 @@ impl f32 { filing an issue describing your use-case too)." )] pub fn abs_sub(self, other: f32) -> f32 { - cmath::fdimf(self, other) + #[allow(deprecated)] + core::f32::abs_sub(self, other) } /// Returns the cube root of a number. @@ -626,7 +622,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f32 { - cmath::cbrtf(self) + core::f32::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 84fd9bfb7b68..051061ae6055 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -46,7 +46,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn floor(self) -> f64 { - unsafe { intrinsics::floorf64(self) } + core::f64::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -68,7 +68,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ceil(self) -> f64 { - unsafe { intrinsics::ceilf64(self) } + core::f64::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -96,7 +96,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn round(self) -> f64 { - unsafe { intrinsics::roundf64(self) } + core::f64::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -122,7 +122,7 @@ impl f64 { #[stable(feature = "round_ties_even", since = "1.77.0")] #[inline] pub fn round_ties_even(self) -> f64 { - intrinsics::round_ties_even_f64(self) + core::f64::round_ties_even(self) } /// Returns the integer part of `self`. @@ -147,7 +147,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn trunc(self) -> f64 { - unsafe { intrinsics::truncf64(self) } + core::f64::trunc(self) } /// Returns the fractional part of `self`. @@ -170,7 +170,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn fract(self) -> f64 { - self - self.trunc() + core::f64::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +212,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f64, b: f64) -> f64 { - unsafe { intrinsics::fmaf64(self, a, b) } + core::f64::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,11 +242,7 @@ impl f64 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f64) -> f64 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q + core::f64::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -283,8 +279,7 @@ impl f64 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f64) -> f64 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } + core::f64::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -312,7 +307,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f64 { - unsafe { intrinsics::powif64(self, n) } + core::f64::powi(self, n) } /// Raises a number to a floating point power. @@ -367,7 +362,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f64 { - unsafe { intrinsics::sqrtf64(self) } + core::f64::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -599,7 +594,8 @@ impl f64 { filing an issue describing your use-case too)." )] pub fn abs_sub(self, other: f64) -> f64 { - cmath::fdim(self, other) + #[allow(deprecated)] + core::f64::abs_sub(self, other) } /// Returns the cube root of a number. @@ -626,7 +622,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f64 { - cmath::cbrt(self) + core::f64::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 0bb40ee4b317..ca04a381271b 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -287,6 +287,7 @@ #![feature(cfi_encoding)] #![feature(char_max_len)] #![feature(concat_idents)] +#![feature(core_float_math)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] @@ -304,7 +305,6 @@ #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(lang_items)] -#![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 1cab04a454dc..1bd3bab5e373 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -307,11 +307,11 @@ impl UnixStream { /// /// ```no_run /// use std::io; - /// use std::net::UdpSocket; + /// use std::os::unix::net::UnixStream; /// use std::time::Duration; /// /// fn main() -> std::io::Result<()> { - /// let socket = UdpSocket::bind("127.0.0.1:34254")?; + /// let socket = UnixStream::connect("/tmp/sock")?; /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); /// let err = result.unwrap_err(); /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 34f0e89f2f1e..5ea91dd6521a 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -72,7 +72,6 @@ pub trait FileExt { /// If this function returns an error, it is unspecified how many bytes it /// has read, but it will never read more than would be necessary to /// completely fill the buffer. - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.read_at(buf, offset) { @@ -144,7 +143,6 @@ pub trait FileExt { /// non-[`io::ErrorKind::Interrupted`] kind that [`write_at`] returns. /// /// [`write_at`]: FileExt::write_at - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.write_at(buf, offset) { diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index a084f452e55d..c223eee95b5f 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -344,6 +344,27 @@ pub trait CommandExt: Sealed { &mut self, attribute_list: &ProcThreadAttributeList<'_>, ) -> io::Result; + + /// When true, sets the `STARTF_RUNFULLSCREEN` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`. + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command; + + /// When true, sets the `STARTF_UNTRUSTEDSOURCE` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`. + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command; + + /// When specified, sets the following flags on the [STARTUPINFO][1] struct before passing it to `CreateProcess`: + /// - If `Some(true)`, sets `STARTF_FORCEONFEEDBACK` + /// - If `Some(false)`, sets `STARTF_FORCEOFFFEEDBACK` + /// - If `None`, does not set any flags + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_force_feedback(&mut self, enabled: Option) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -385,6 +406,21 @@ impl CommandExt for process::Command { .spawn_with_attributes(sys::process::Stdio::Inherit, true, Some(attribute_list)) .map(process::Child::from_inner) } + + fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command { + self.as_inner_mut().startupinfo_fullscreen(enabled); + self + } + + fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command { + self.as_inner_mut().startupinfo_untrusted_source(enabled); + self + } + + fn startupinfo_force_feedback(&mut self, enabled: Option) -> &mut process::Command { + self.as_inner_mut().startupinfo_force_feedback(enabled); + self + } } #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 9737b2f5bfe6..b3f3b301e3db 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -26,6 +26,13 @@ use crate::sync::Once; use crate::thread::{self, main_thread}; use crate::{mem, panic, sys}; +// This function is needed by the panic runtime. +#[cfg(not(test))] +#[rustc_std_internal_symbol] +fn __rust_abort() { + crate::process::abort(); +} + // Prints to the "panic output", depending on the platform this may be: // - the standard error output // - some dedicated platform specific output @@ -47,7 +54,7 @@ macro_rules! rtabort { ($($t:tt)*) => { { rtprintpanic!("fatal runtime error: {}, aborting\n", format_args!($($t)*)); - crate::sys::abort_internal(); + crate::process::abort(); } } } diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 668fd9285340..299ce1a6ff06 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -7,13 +7,9 @@ unsafe extern "C" { pub safe fn asin(n: f64) -> f64; pub safe fn atan(n: f64) -> f64; pub safe fn atan2(a: f64, b: f64) -> f64; - pub safe fn cbrt(n: f64) -> f64; - pub safe fn cbrtf(n: f32) -> f32; pub safe fn cosh(n: f64) -> f64; pub safe fn expm1(n: f64) -> f64; pub safe fn expm1f(n: f32) -> f32; - pub safe fn fdim(a: f64, b: f64) -> f64; - pub safe fn fdimf(a: f32, b: f32) -> f32; #[cfg_attr(target_env = "msvc", link_name = "_hypot")] pub safe fn hypot(x: f64, y: f64) -> f64; #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 863358596c19..a3e520fdeef4 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1498,11 +1498,10 @@ impl File { None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx"))] { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. - // `futimens` and `UTIME_OMIT` are a work in progress for vxworks. let _ = times; Err(io::const_error!( io::ErrorKind::Unsupported, diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index ea636938d703..fb8d69b73759 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -43,15 +43,6 @@ pub fn abort_internal() -> ! { unsafe { hermit_abi::abort() } } -// This function is needed by the panic runtime. The symbol is named in -// pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -#[unsafe(no_mangle)] -// NB. used by both libunwind and libpanic_abort -pub extern "C" fn __rust_abort() { - abort_internal(); -} - // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 3932f64c0ef4..6e43a79ddec2 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -112,11 +112,14 @@ pub fn abort_internal() -> ! { abi::usercalls::exit(true) } -// This function is needed by the panic runtime. The symbol is named in +// This function is needed by libunwind. The symbol is named in // pre-link args for the target specification, so keep that in sync. +// Note: contrary to the `__rust_abort` in `crate::rt`, this uses `no_mangle` +// because it is actually used from C code. Because symbols annotated with +// #[rustc_std_internal_symbol] get mangled, this will not lead to linker +// conflicts. #[cfg(not(test))] #[unsafe(no_mangle)] -// NB. used by both libunwind and libpanic_abort pub extern "C" fn __rust_abort() { abort_internal(); } diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 78fcfcb3b77d..8911a2ee5194 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -161,14 +161,6 @@ pub fn abort_internal() -> ! { core::intrinsics::abort(); } -// This function is needed by the panic runtime. The symbol is named in -// pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -#[unsafe(no_mangle)] -pub extern "C" fn __rust_abort() { - abort_internal(); -} - /// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 8bf6d8335159..a3be2cdf738f 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -25,15 +25,36 @@ impl Drop for Handler { } } -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ), +))] +mod thread_info; + +// miri doesn't model signals nor stack overflows and this code has some +// synchronization properties that we don't want to expose to user code, +// hence we disable it on miri. +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ) ))] mod imp { use libc::{ @@ -46,22 +67,13 @@ mod imp { use libc::{mmap64, mprotect, munmap}; use super::Handler; - use crate::cell::Cell; + use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::{io, mem, ptr, thread}; - - // We use a TLS variable to store the address of the guard page. While TLS - // variables are not guaranteed to be signal-safe, this works out in practice - // since we make sure to write to the variable before the signal stack is - // installed, thereby ensuring that the variable is always allocated when - // the signal handler is called. - thread_local! { - // FIXME: use `Range` once that implements `Copy`. - static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; - } + use crate::thread::with_current_name; + use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -93,29 +105,35 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let (start, end) = GUARD.get(); // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. - let addr = unsafe { (*info).si_addr().addr() }; + let fault_addr = unsafe { (*info).si_addr().addr() }; - // If the faulting address is within the guard page, then we print a - // message saying so and abort. - if start <= addr && addr < end { - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); - }); - - rtabort!("stack overflow"); - } else { - // Unregister ourselves by reverting back to the default behavior. - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" - let mut action: sigaction = unsafe { mem::zeroed() }; - action.sa_sigaction = SIG_DFL; - // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction - unsafe { sigaction(signum, &action, ptr::null_mut()) }; - - // See comment above for why this function returns. + // `with_current_info` expects that the process aborts after it is + // called. If the signal was not caused by a memory access, this might + // not be true. We detect this by noticing that the `si_addr` field is + // zero if the signal is synthetic. + if fault_addr != 0 { + with_current_info(|thread_info| { + // If the faulting address is within the guard page, then we print a + // message saying so and abort. + if let Some(thread_info) = thread_info + && thread_info.guard_page_range.contains(&fault_addr) + { + let name = thread_info.thread_name.as_deref().unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + rtabort!("stack overflow"); + } + }) } + + // Unregister ourselves by reverting back to the default behavior. + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; + action.sa_sigaction = SIG_DFL; + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; + + // See comment above for why this function returns. } static PAGE_SIZE: Atomic = AtomicUsize::new(0); @@ -128,9 +146,7 @@ mod imp { pub unsafe fn init() { PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { install_main_guard().unwrap_or(0..0) }; - GUARD.set((guard.start, guard.end)); + let mut guard_page_range = unsafe { install_main_guard() }; // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" let mut action: sigaction = unsafe { mem::zeroed() }; @@ -145,7 +161,13 @@ mod imp { let handler = unsafe { make_handler(true) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); + + if let Some(guard_page_range) = guard_page_range.take() { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } + action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; // SAFETY: only overriding signals if the default is set @@ -214,9 +236,10 @@ mod imp { } if !main_thread { - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { current_guard() }.unwrap_or(0..0); - GUARD.set((guard.start, guard.end)); + if let Some(guard_page_range) = unsafe { current_guard() } { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } // SAFETY: assuming stack_t is zero-initializable @@ -261,6 +284,8 @@ mod imp { // a mapping that started one page earlier, so walk back a page and unmap from there. unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } + + delete_current_info(); } /// Modern kernels on modern hardware can have dynamic signal stack sizes. @@ -590,17 +615,20 @@ mod imp { // usually have fewer qualms about forwards compatibility, since the runtime // is shipped with the OS): // -#[cfg(not(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - target_os = "cygwin", -)))] +#[cfg(any( + miri, + not(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "cygwin", + )) +))] mod imp { pub unsafe fn init() {} diff --git a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs new file mode 100644 index 000000000000..e81429b98a6c --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs @@ -0,0 +1,129 @@ +//! TLS, but async-signal-safe. +//! +//! Unfortunately, because thread local storage isn't async-signal-safe, we +//! cannot soundly use it in our stack overflow handler. While this works +//! without problems on most platforms, it can lead to undefined behaviour +//! on others (such as GNU/Linux). Luckily, the POSIX specification documents +//! two thread-specific values that can be accessed in asynchronous signal +//! handlers: the value of `pthread_self()` and the address of `errno`. As +//! `pthread_t` is an opaque platform-specific type, we use the address of +//! `errno` here. As it is thread-specific and does not change over the +//! lifetime of a thread, we can use `&errno` as a key for a `BTreeMap` +//! that stores thread-specific data. +//! +//! Concurrent access to this map is synchronized by two locks – an outer +//! [`Mutex`] and an inner spin lock that also remembers the identity of +//! the lock owner: +//! * The spin lock is the primary means of synchronization: since it only +//! uses native atomics, it can be soundly used inside the signal handle +//! as opposed to [`Mutex`], which might not be async-signal-safe. +//! * The [`Mutex`] prevents busy-waiting in the setup logic, as all accesses +//! there are performed with the [`Mutex`] held, which makes the spin-lock +//! redundant in the common case. +//! * Finally, by using the `errno` address as the locked value of the spin +//! lock, we can detect cases where a SIGSEGV occurred while the thread +//! info is being modified. + +use crate::collections::BTreeMap; +use crate::hint::spin_loop; +use crate::ops::Range; +use crate::sync::Mutex; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sys::os::errno_location; + +pub struct ThreadInfo { + pub guard_page_range: Range, + pub thread_name: Option>, +} + +static LOCK: Mutex<()> = Mutex::new(()); +static SPIN_LOCK: AtomicUsize = AtomicUsize::new(0); +// This uses a `BTreeMap` instead of a hashmap since it supports constant +// initialization and automatically reduces the amount of memory used when +// items are removed. +static mut THREAD_INFO: BTreeMap = BTreeMap::new(); + +struct UnlockOnDrop; + +impl Drop for UnlockOnDrop { + fn drop(&mut self) { + SPIN_LOCK.store(0, Ordering::Release); + } +} + +/// Get the current thread's information, if available. +/// +/// Calling this function might freeze other threads if they attempt to modify +/// their thread information. Thus, the caller should ensure that the process +/// is aborted shortly after this function is called. +/// +/// This function is guaranteed to be async-signal-safe if `f` is too. +pub fn with_current_info(f: impl FnOnce(Option<&ThreadInfo>) -> R) -> R { + let this = errno_location().addr(); + let mut attempt = 0; + let _guard = loop { + // If we are just spinning endlessly, it's very likely that the thread + // modifying the thread info map has a lower priority than us and will + // not continue until we stop running. Just give up in that case. + if attempt == 10_000_000 { + rtprintpanic!("deadlock in SIGSEGV handler"); + return f(None); + } + + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => break UnlockOnDrop, + Err(owner) if owner == this => { + rtabort!("a thread received SIGSEGV while modifying its stack overflow information") + } + // Spin until the lock can be acquired – there is nothing better to + // do. This is unfortunately a priority hole, but a stack overflow + // is a fatal error anyway. + Err(_) => { + spin_loop(); + attempt += 1; + } + } + }; + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &*(&raw const THREAD_INFO) }; + f(thread_info.get(&this)) +} + +fn spin_lock_in_setup(this: usize) -> UnlockOnDrop { + loop { + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return UnlockOnDrop, + Err(owner) if owner == this => { + unreachable!("the thread info setup logic isn't recursive") + } + // This function is always called with the outer lock held, + // meaning the only time locking can fail is if another thread has + // encountered a stack overflow. Since that will abort the process, + // we just stop the current thread until that time. We use `pause` + // instead of spinning to avoid priority inversion. + // SAFETY: this doesn't have any safety preconditions. + Err(_) => drop(unsafe { libc::pause() }), + } + } +} + +pub fn set_current_info(guard_page_range: Range, thread_name: Option>) { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.insert(this, ThreadInfo { guard_page_range, thread_name }); +} + +pub fn delete_current_info() { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.remove(&this); +} diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index afda7c65e108..d8b189413f4a 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -222,16 +222,8 @@ impl Thread { #[cfg(target_os = "vxworks")] pub fn set_name(name: &CStr) { - // FIXME(libc): adding real STATUS, ERROR type eventually. - unsafe extern "C" { - fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; - } - - // VX_TASK_NAME_LEN is 31 in VxWorks 7. - const VX_TASK_NAME_LEN: usize = 31; - - let mut name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); - let res = unsafe { taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + let mut name = truncate_cstr::<{ libc::VX_TASK_RENAME_LENGTH - 1 }>(name); + let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; debug_assert_eq!(res, libc::OK); } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4f18c4009ab6..8f54e2376eb8 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -328,8 +328,12 @@ pub fn dur2timeout(dur: Duration) -> u32 { /// Use `__fastfail` to abort the process /// -/// This is the same implementation as in libpanic_abort's `__rust_start_panic`. See -/// that function for more information on `__fastfail` +/// In Windows 8 and later, this will terminate the process immediately without +/// running any in-process exception handlers. In earlier versions of Windows, +/// this sequence of instructions will be treated as an access violation, which +/// will still terminate the process but might run some exception handlers. +/// +/// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail #[cfg(not(miri))] // inline assembly does not work in Miri pub fn abort_internal() -> ! { unsafe { diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 383d031ed435..042c4ff862ff 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,5 +1,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] +use crate::os::xous::ffi::exit; + pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -9,3 +11,7 @@ pub mod time; #[path = "../unsupported/common.rs"] mod common; pub use common::*; + +pub fn abort_internal() -> ! { + exit(101); +} diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 2230dabe096f..d612a27d2bdb 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -62,14 +62,6 @@ mod c_compat { } exit(unsafe { main() }); } - - // This function is needed by the panic runtime. The symbol is named in - // pre-link args for the target specification, so keep that in sync. - #[unsafe(no_mangle)] - // NB. used by both libunwind and libpanic_abort - pub extern "C" fn __rust_abort() -> ! { - exit(101); - } } pub fn errno() -> i32 { diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 4acd753eec91..1ee3fbd285f5 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -155,6 +155,9 @@ pub struct Command { stdout: Option, stderr: Option, force_quotes_enabled: bool, + startupinfo_fullscreen: bool, + startupinfo_untrusted_source: bool, + startupinfo_force_feedback: Option, } pub enum Stdio { @@ -186,6 +189,9 @@ impl Command { stdout: None, stderr: None, force_quotes_enabled: false, + startupinfo_fullscreen: false, + startupinfo_untrusted_source: false, + startupinfo_force_feedback: None, } } @@ -222,6 +228,18 @@ impl Command { self.args.push(Arg::Raw(command_str_to_append.to_os_string())) } + pub fn startupinfo_fullscreen(&mut self, enabled: bool) { + self.startupinfo_fullscreen = enabled; + } + + pub fn startupinfo_untrusted_source(&mut self, enabled: bool) { + self.startupinfo_untrusted_source = enabled; + } + + pub fn startupinfo_force_feedback(&mut self, enabled: Option) { + self.startupinfo_force_feedback = enabled; + } + pub fn get_program(&self) -> &OsStr { &self.program } @@ -343,6 +361,24 @@ impl Command { si.wShowWindow = cmd_show; } + if self.startupinfo_fullscreen { + si.dwFlags |= c::STARTF_RUNFULLSCREEN; + } + + if self.startupinfo_untrusted_source { + si.dwFlags |= c::STARTF_UNTRUSTEDSOURCE; + } + + match self.startupinfo_force_feedback { + Some(true) => { + si.dwFlags |= c::STARTF_FORCEONFEEDBACK; + } + Some(false) => { + si.dwFlags |= c::STARTF_FORCEOFFFEEDBACK; + } + None => {} + } + let si_ptr: *mut c::STARTUPINFOW; let mut si_ex; diff --git a/library/std/tests/floats/f128.rs b/library/std/tests/floats/f128.rs index c2618f3b315e..e7c90faa05c2 100644 --- a/library/std/tests/floats/f128.rs +++ b/library/std/tests/floats/f128.rs @@ -2,49 +2,26 @@ #![cfg(target_has_reliable_f128)] use std::f128::consts; -use std::num::FpCategory as Fp; -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -use std::ops::Rem; use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +const TOL: f128 = 1e-12; + /// For operations that are near exact, usually not involving math of different /// signs. const TOL_PRECISE: f128 = 1e-28; -/// Default tolerances. Works for values that should be near precise but not exact. Roughly -/// the precision carried by `100 * 100`. -const TOL: f128 = 1e-12; - /// Tolerances for math that is allowed to be imprecise, usually due to multiple chained /// operations. #[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] const TOL_IMPR: f128 = 1e-10; -/// Smallest number -const TINY_BITS: u128 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u128 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; - -/// First pattern over the mantissa -const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u128 = 0x00005555555555555555555555555555; - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f128_biteq { @@ -68,456 +45,8 @@ fn test_num_f128() { assert_eq!(ten.div(two), ten / two); } -// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support -// the intrinsics. - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_num_f128_rem() { - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.rem(two), ten % two); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_min_nan() { - assert_eq!(f128::NAN.min(2.0), 2.0); - assert_eq!(2.0f128.min(f128::NAN), 2.0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_max_nan() { - assert_eq!(f128::NAN.max(2.0), 2.0); - assert_eq!(2.0f128.max(f128::NAN), 2.0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_minimum() { - assert!(f128::NAN.minimum(2.0).is_nan()); - assert!(2.0f128.minimum(f128::NAN).is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_maximum() { - assert!(f128::NAN.maximum(2.0).is_nan()); - assert!(2.0f128.maximum(f128::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f128 = f128::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f128::MANTISSA_DIGITS - 2)) != 0); -} - -#[test] -fn test_infinity() { - let inf: f128 = f128::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f128 = 0.0f128; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f128 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f128 = 1.0f128; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f128.is_nan()); - assert!(!5.3f128.is_nan()); - assert!(!(-10.732f128).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f128.is_infinite()); - assert!(!42.8f128.is_infinite()); - assert!(!(-109.2f128).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f128.is_finite()); - assert!(42.8f128.is_finite()); - assert!((-109.2f128).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let zero: f128 = 0.0f128; - let neg_zero: f128 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f128.is_normal()); - assert!(1e-4931f128.is_normal()); - assert!(!1e-4932f128.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let zero: f128 = 0.0f128; - let neg_zero: f128 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f128.classify(), Fp::Normal); - assert_eq!(1e-4931f128.classify(), Fp::Normal); - assert_eq!(1e-4932f128.classify(), Fp::Subnormal); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_floor() { - assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_ceil() { - assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_round() { - assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_round_ties_even() { - assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_trunc() { - assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_fract() { - assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_abs() { - assert_eq!(f128::INFINITY.abs(), f128::INFINITY); - assert_eq!(1f128.abs(), 1f128); - assert_eq!(0f128.abs(), 0f128); - assert_eq!((-0f128).abs(), 0f128); - assert_eq!((-1f128).abs(), 1f128); - assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); - assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); - assert!(f128::NAN.abs().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f128::INFINITY.is_sign_positive()); - assert!(1f128.is_sign_positive()); - assert!(0f128.is_sign_positive()); - assert!(!(-0f128).is_sign_positive()); - assert!(!(-1f128).is_sign_positive()); - assert!(!f128::NEG_INFINITY.is_sign_positive()); - assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); - assert!(f128::NAN.is_sign_positive()); - assert!(!(-f128::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f128::INFINITY.is_sign_negative()); - assert!(!1f128.is_sign_negative()); - assert!(!0f128.is_sign_negative()); - assert!((-0f128).is_sign_negative()); - assert!((-1f128).is_sign_negative()); - assert!(f128::NEG_INFINITY.is_sign_negative()); - assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); - assert!(!f128::NAN.is_sign_negative()); - assert!((-f128::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); - assert_f128_biteq!(f128::MIN.next_up(), -max_down); - assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); - assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f128_biteq!((-tiny_up).next_up(), -tiny); - assert_f128_biteq!((-tiny).next_up(), -0.0f128); - assert_f128_biteq!((-0.0f128).next_up(), tiny); - assert_f128_biteq!(0.0f128.next_up(), tiny); - assert_f128_biteq!(tiny.next_up(), tiny_up); - assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); - assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); - assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_up(), nan0); - assert_f128_biteq!(nan1.next_up(), nan1); - assert_f128_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!((-max_down).next_down(), f128::MIN); - assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); - assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f128_biteq!((-tiny).next_down(), -tiny_up); - assert_f128_biteq!((-0.0f128).next_down(), -tiny); - assert_f128_biteq!((0.0f128).next_down(), -tiny); - assert_f128_biteq!(tiny.next_down(), 0.0f128); - assert_f128_biteq!(tiny_up.next_down(), tiny); - assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); - assert_f128_biteq!(f128::MAX.next_down(), max_down); - assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_down(), nan0); - assert_f128_biteq!(nan1.next_down(), nan1); - assert_f128_biteq!(nan2.next_down(), nan2); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_mul_add() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); - assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); - assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); - assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f128.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_recip() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.recip(), 1.0); - assert_eq!(2.0f128.recip(), 0.5); - assert_eq!((-0.4f128).recip(), -2.5); - assert_eq!(0.0f128.recip(), inf); - assert_approx_eq!( - f128::MAX.recip(), - 8.40525785778023376565669454330438228902076605e-4933, - 1e-4900 - ); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - // Many math functions allow for less accurate results, so the next tolerance up is used -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_powi() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powi(1), 1.0); - assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); - assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); - assert_eq!(8.3f128.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] @@ -536,19 +65,6 @@ fn test_powf() { assert_eq!(neg_inf.powf(3.0), neg_inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_sqrt_domain() { - assert!(f128::NAN.sqrt().is_nan()); - assert!(f128::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f128).sqrt().is_nan()); - assert_eq!((-0.0f128).sqrt(), -0.0); - assert_eq!(0.0f128.sqrt(), 0.0); - assert_eq!(1.0f128.sqrt(), 1.0); - assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] @@ -654,38 +170,6 @@ fn test_log10() { assert_eq!(0.0f128.log10(), neg_inf); } -#[test] -fn test_to_degrees() { - let pi: f128 = consts::PI; - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_degrees(), 0.0); - assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); - assert_approx_eq!(pi.to_degrees(), 180.0, TOL); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f128 = consts::PI; - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_radians(), 0.0); - assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); - assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); - // check approx rather than exact because round trip for pi doesn't fall on an exactly - // representable value (unlike `f32` and `f64`). - assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] @@ -835,237 +319,3 @@ fn test_real_consts() { assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); } } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); - assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); - assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); - assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; - assert!(f128::from_bits(masked_nan1).is_nan()); - assert!(f128::from_bits(masked_nan2).is_nan()); - - assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f128.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f128.clamp(f128::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f128.clamp(3.0, f128::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u128 { - 1 << (f128::MANTISSA_DIGITS - 2) - } - - // FIXME(f16_f128): test subnormals when powf is available - // fn min_subnorm() -> f128 { - // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) - // } - - // fn max_subnorm() -> f128 { - // f128::MIN_POSITIVE - min_subnorm() - // } - - fn q_nan() -> f128 { - f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f128 { - f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); - // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); - // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); - // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - -#[test] -fn test_algebraic() { - let a: f128 = 123.0; - let b: f128 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps); - assert_approx_eq!(a.algebraic_div(b), a / b, eps); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps); -} - -#[test] -fn test_from() { - assert_eq!(f128::from(false), 0.0); - assert_eq!(f128::from(true), 1.0); - assert_eq!(f128::from(u8::MIN), 0.0); - assert_eq!(f128::from(42_u8), 42.0); - assert_eq!(f128::from(u8::MAX), 255.0); - assert_eq!(f128::from(i8::MIN), -128.0); - assert_eq!(f128::from(42_i8), 42.0); - assert_eq!(f128::from(i8::MAX), 127.0); - assert_eq!(f128::from(u16::MIN), 0.0); - assert_eq!(f128::from(42_u16), 42.0); - assert_eq!(f128::from(u16::MAX), 65535.0); - assert_eq!(f128::from(i16::MIN), -32768.0); - assert_eq!(f128::from(42_i16), 42.0); - assert_eq!(f128::from(i16::MAX), 32767.0); - assert_eq!(f128::from(u32::MIN), 0.0); - assert_eq!(f128::from(42_u32), 42.0); - assert_eq!(f128::from(u32::MAX), 4294967295.0); - assert_eq!(f128::from(i32::MIN), -2147483648.0); - assert_eq!(f128::from(42_i32), 42.0); - assert_eq!(f128::from(i32::MAX), 2147483647.0); - // FIXME(f16_f128): Uncomment these tests once the From<{u64,i64}> impls are added. - // assert_eq!(f128::from(u64::MIN), 0.0); - // assert_eq!(f128::from(42_u64), 42.0); - // assert_eq!(f128::from(u64::MAX), 18446744073709551615.0); - // assert_eq!(f128::from(i64::MIN), -9223372036854775808.0); - // assert_eq!(f128::from(42_i64), 42.0); - // assert_eq!(f128::from(i64::MAX), 9223372036854775807.0); -} diff --git a/library/std/tests/floats/f16.rs b/library/std/tests/floats/f16.rs index 70bbcd07160e..0f8b4138d226 100644 --- a/library/std/tests/floats/f16.rs +++ b/library/std/tests/floats/f16.rs @@ -2,7 +2,6 @@ #![cfg(target_has_reliable_f16)] use std::f16::consts; -use std::num::FpCategory as Fp; /// Tolerance for results on the order of 10.0e-2 #[allow(unused)] @@ -20,27 +19,6 @@ const TOL_P2: f16 = 0.5; #[allow(unused)] const TOL_P4: f16 = 10.0; -/// Smallest number -const TINY_BITS: u16 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u16 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u16 = 0x7bfe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u16 = 0x0400; - -/// First pattern over the mantissa -const NAN_MASK1: u16 = 0x02aa; - -/// Second pattern over the mantissa -const NAN_MASK2: u16 = 0x0155; - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f16_biteq { @@ -52,446 +30,6 @@ macro_rules! assert_f16_biteq { }; } -#[test] -fn test_num_f16() { - crate::test_num(10f16, 2f16); -} - -// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support -// the intrinsics. - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_min_nan() { - assert_eq!(f16::NAN.min(2.0), 2.0); - assert_eq!(2.0f16.min(f16::NAN), 2.0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_max_nan() { - assert_eq!(f16::NAN.max(2.0), 2.0); - assert_eq!(2.0f16.max(f16::NAN), 2.0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_minimum() { - assert!(f16::NAN.minimum(2.0).is_nan()); - assert!(2.0f16.minimum(f16::NAN).is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_maximum() { - assert!(f16::NAN.maximum(2.0).is_nan()); - assert!(2.0f16.maximum(f16::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f16 = f16::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f16::MANTISSA_DIGITS - 2)) != 0); -} - -#[test] -fn test_infinity() { - let inf: f16 = f16::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f16 = 0.0f16; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f16 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f16 = 1.0f16; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f16.is_nan()); - assert!(!5.3f16.is_nan()); - assert!(!(-10.732f16).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f16.is_infinite()); - assert!(!42.8f16.is_infinite()); - assert!(!(-109.2f16).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f16.is_finite()); - assert!(42.8f16.is_finite()); - assert!((-109.2f16).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let zero: f16 = 0.0f16; - let neg_zero: f16 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f16.is_normal()); - assert!(1e-4f16.is_normal()); - assert!(!1e-5f16.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let zero: f16 = 0.0f16; - let neg_zero: f16 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f16.classify(), Fp::Normal); - assert_eq!(1e-4f16.classify(), Fp::Normal); - assert_eq!(1e-5f16.classify(), Fp::Subnormal); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_floor() { - assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_ceil() { - assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_round() { - assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); - assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_round_ties_even() { - assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_trunc() { - assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_fract() { - assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); - assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); - assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); - assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); - assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); - assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_abs() { - assert_eq!(f16::INFINITY.abs(), f16::INFINITY); - assert_eq!(1f16.abs(), 1f16); - assert_eq!(0f16.abs(), 0f16); - assert_eq!((-0f16).abs(), 0f16); - assert_eq!((-1f16).abs(), 1f16); - assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); - assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); - assert!(f16::NAN.abs().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f16::INFINITY.is_sign_positive()); - assert!(1f16.is_sign_positive()); - assert!(0f16.is_sign_positive()); - assert!(!(-0f16).is_sign_positive()); - assert!(!(-1f16).is_sign_positive()); - assert!(!f16::NEG_INFINITY.is_sign_positive()); - assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); - assert!(f16::NAN.is_sign_positive()); - assert!(!(-f16::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f16::INFINITY.is_sign_negative()); - assert!(!1f16.is_sign_negative()); - assert!(!0f16.is_sign_negative()); - assert!((-0f16).is_sign_negative()); - assert!((-1f16).is_sign_negative()); - assert!(f16::NEG_INFINITY.is_sign_negative()); - assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); - assert!(!f16::NAN.is_sign_negative()); - assert!((-f16::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); - assert_f16_biteq!(f16::MIN.next_up(), -max_down); - assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); - assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f16_biteq!((-tiny_up).next_up(), -tiny); - assert_f16_biteq!((-tiny).next_up(), -0.0f16); - assert_f16_biteq!((-0.0f16).next_up(), tiny); - assert_f16_biteq!(0.0f16.next_up(), tiny); - assert_f16_biteq!(tiny.next_up(), tiny_up); - assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); - assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); - assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_up(), nan0); - assert_f16_biteq!(nan1.next_up(), nan1); - assert_f16_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!((-max_down).next_down(), f16::MIN); - assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); - assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f16_biteq!((-tiny).next_down(), -tiny_up); - assert_f16_biteq!((-0.0f16).next_down(), -tiny); - assert_f16_biteq!((0.0f16).next_down(), -tiny); - assert_f16_biteq!(tiny.next_down(), 0.0f16); - assert_f16_biteq!(tiny_up.next_down(), tiny); - assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); - assert_f16_biteq!(f16::MAX.next_down(), max_down); - assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_down(), nan0); - assert_f16_biteq!(nan1.next_down(), nan1); - assert_f16_biteq!(nan2.next_down(), nan2); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_mul_add() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); - assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); - assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); - assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f16.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_recip() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.recip(), 1.0); - assert_eq!(2.0f16.recip(), 0.5); - assert_eq!((-0.4f16).recip(), -2.5); - assert_eq!(0.0f16.recip(), inf); - assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_powi() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.powi(1), 1.0); - assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); - assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); - assert_eq!(8.3f16.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f16_math)] @@ -510,19 +48,6 @@ fn test_powf() { assert_eq!(neg_inf.powf(3.0), neg_inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_sqrt_domain() { - assert!(f16::NAN.sqrt().is_nan()); - assert!(f16::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f16).sqrt().is_nan()); - assert_eq!((-0.0f16).sqrt(), -0.0); - assert_eq!(0.0f16.sqrt(), 0.0); - assert_eq!(1.0f16.sqrt(), 1.0); - assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f16_math)] @@ -628,36 +153,6 @@ fn test_log10() { assert_eq!(0.0f16.log10(), neg_inf); } -#[test] -fn test_to_degrees() { - let pi: f16 = consts::PI; - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_degrees(), 0.0); - assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); - assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f16 = consts::PI; - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_radians(), 0.0); - assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); - assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); - assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f16_math)] @@ -803,220 +298,3 @@ fn test_real_consts() { assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); } } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f16).to_bits(), 0x3c00); - assert_eq!((12.5f16).to_bits(), 0x4a40); - assert_eq!((1337f16).to_bits(), 0x6539); - assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; - assert!(f16::from_bits(masked_nan1).is_nan()); - assert!(f16::from_bits(masked_nan2).is_nan()); - - assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f16.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f16.clamp(f16::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f16.clamp(3.0, f16::NAN); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u16 { - 1 << (f16::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f16 { - f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) - } - - fn max_subnorm() -> f16 { - f16::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f16 { - f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f16 { - f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - -#[test] -fn test_algebraic() { - let a: f16 = 123.0; - let b: f16 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps_add = if cfg!(miri) { 1e1 } else { 0.0 }; - let eps_mul = if cfg!(miri) { 1e3 } else { 0.0 }; - let eps_div = if cfg!(miri) { 1e0 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); - assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); -} - -#[test] -fn test_from() { - assert_eq!(f16::from(false), 0.0); - assert_eq!(f16::from(true), 1.0); - assert_eq!(f16::from(u8::MIN), 0.0); - assert_eq!(f16::from(42_u8), 42.0); - assert_eq!(f16::from(u8::MAX), 255.0); - assert_eq!(f16::from(i8::MIN), -128.0); - assert_eq!(f16::from(42_i8), 42.0); - assert_eq!(f16::from(i8::MAX), 127.0); -} diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs index 9af23afc5bbf..e54f227bb774 100644 --- a/library/std/tests/floats/f32.rs +++ b/library/std/tests/floats/f32.rs @@ -1,26 +1,4 @@ use std::f32::consts; -use std::num::FpCategory as Fp; - -/// Smallest number -const TINY_BITS: u32 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u32 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; - -/// First pattern over the mantissa -const NAN_MASK1: u32 = 0x002a_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u32 = 0x0055_5555; #[allow(unused_macros)] macro_rules! assert_f32_biteq { @@ -33,426 +11,6 @@ macro_rules! assert_f32_biteq { }; } -#[test] -fn test_num_f32() { - crate::test_num(10f32, 2f32); -} - -#[test] -fn test_min_nan() { - assert_eq!(f32::NAN.min(2.0), 2.0); - assert_eq!(2.0f32.min(f32::NAN), 2.0); -} - -#[test] -fn test_max_nan() { - assert_eq!(f32::NAN.max(2.0), 2.0); - assert_eq!(2.0f32.max(f32::NAN), 2.0); -} - -#[test] -fn test_minimum() { - assert!(f32::NAN.minimum(2.0).is_nan()); - assert!(2.0f32.minimum(f32::NAN).is_nan()); -} - -#[test] -fn test_maximum() { - assert!(f32::NAN.maximum(2.0).is_nan()); - assert!(2.0f32.maximum(f32::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f32 = f32::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f32::MANTISSA_DIGITS - 2)) != 0); -} - -#[test] -fn test_infinity() { - let inf: f32 = f32::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f32 = 0.0f32; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f32 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f32 = 1.0f32; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f32.is_nan()); - assert!(!5.3f32.is_nan()); - assert!(!(-10.732f32).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f32.is_infinite()); - assert!(!42.8f32.is_infinite()); - assert!(!(-109.2f32).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f32.is_finite()); - assert!(42.8f32.is_finite()); - assert!((-109.2f32).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let zero: f32 = 0.0f32; - let neg_zero: f32 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f32.is_normal()); - assert!(1e-37f32.is_normal()); - assert!(!1e-38f32.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let zero: f32 = 0.0f32; - let neg_zero: f32 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f32.classify(), Fp::Normal); - assert_eq!(1e-37f32.classify(), Fp::Normal); - assert_eq!(1e-38f32.classify(), Fp::Subnormal); -} - -#[test] -fn test_floor() { - assert_approx_eq!(1.0f32.floor(), 1.0f32); - assert_approx_eq!(1.3f32.floor(), 1.0f32); - assert_approx_eq!(1.5f32.floor(), 1.0f32); - assert_approx_eq!(1.7f32.floor(), 1.0f32); - assert_approx_eq!(0.0f32.floor(), 0.0f32); - assert_approx_eq!((-0.0f32).floor(), -0.0f32); - assert_approx_eq!((-1.0f32).floor(), -1.0f32); - assert_approx_eq!((-1.3f32).floor(), -2.0f32); - assert_approx_eq!((-1.5f32).floor(), -2.0f32); - assert_approx_eq!((-1.7f32).floor(), -2.0f32); -} - -#[test] -fn test_ceil() { - assert_approx_eq!(1.0f32.ceil(), 1.0f32); - assert_approx_eq!(1.3f32.ceil(), 2.0f32); - assert_approx_eq!(1.5f32.ceil(), 2.0f32); - assert_approx_eq!(1.7f32.ceil(), 2.0f32); - assert_approx_eq!(0.0f32.ceil(), 0.0f32); - assert_approx_eq!((-0.0f32).ceil(), -0.0f32); - assert_approx_eq!((-1.0f32).ceil(), -1.0f32); - assert_approx_eq!((-1.3f32).ceil(), -1.0f32); - assert_approx_eq!((-1.5f32).ceil(), -1.0f32); - assert_approx_eq!((-1.7f32).ceil(), -1.0f32); -} - -#[test] -fn test_round() { - assert_approx_eq!(2.5f32.round(), 3.0f32); - assert_approx_eq!(1.0f32.round(), 1.0f32); - assert_approx_eq!(1.3f32.round(), 1.0f32); - assert_approx_eq!(1.5f32.round(), 2.0f32); - assert_approx_eq!(1.7f32.round(), 2.0f32); - assert_approx_eq!(0.0f32.round(), 0.0f32); - assert_approx_eq!((-0.0f32).round(), -0.0f32); - assert_approx_eq!((-1.0f32).round(), -1.0f32); - assert_approx_eq!((-1.3f32).round(), -1.0f32); - assert_approx_eq!((-1.5f32).round(), -2.0f32); - assert_approx_eq!((-1.7f32).round(), -2.0f32); -} - -#[test] -fn test_round_ties_even() { - assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32); - assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32); - assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32); - assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32); - assert_approx_eq!((-1.7f32).round_ties_even(), -2.0f32); -} - -#[test] -fn test_trunc() { - assert_approx_eq!(1.0f32.trunc(), 1.0f32); - assert_approx_eq!(1.3f32.trunc(), 1.0f32); - assert_approx_eq!(1.5f32.trunc(), 1.0f32); - assert_approx_eq!(1.7f32.trunc(), 1.0f32); - assert_approx_eq!(0.0f32.trunc(), 0.0f32); - assert_approx_eq!((-0.0f32).trunc(), -0.0f32); - assert_approx_eq!((-1.0f32).trunc(), -1.0f32); - assert_approx_eq!((-1.3f32).trunc(), -1.0f32); - assert_approx_eq!((-1.5f32).trunc(), -1.0f32); - assert_approx_eq!((-1.7f32).trunc(), -1.0f32); -} - -#[test] -fn test_fract() { - assert_approx_eq!(1.0f32.fract(), 0.0f32); - assert_approx_eq!(1.3f32.fract(), 0.3f32); - assert_approx_eq!(1.5f32.fract(), 0.5f32); - assert_approx_eq!(1.7f32.fract(), 0.7f32); - assert_approx_eq!(0.0f32.fract(), 0.0f32); - assert_approx_eq!((-0.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.3f32).fract(), -0.3f32); - assert_approx_eq!((-1.5f32).fract(), -0.5f32); - assert_approx_eq!((-1.7f32).fract(), -0.7f32); -} - -#[test] -fn test_abs() { - assert_eq!(f32::INFINITY.abs(), f32::INFINITY); - assert_eq!(1f32.abs(), 1f32); - assert_eq!(0f32.abs(), 0f32); - assert_eq!((-0f32).abs(), 0f32); - assert_eq!((-1f32).abs(), 1f32); - assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); - assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); - assert!(f32::NAN.abs().is_nan()); -} - -#[test] -fn test_signum() { - assert_eq!(f32::INFINITY.signum(), 1f32); - assert_eq!(1f32.signum(), 1f32); - assert_eq!(0f32.signum(), 1f32); - assert_eq!((-0f32).signum(), -1f32); - assert_eq!((-1f32).signum(), -1f32); - assert_eq!(f32::NEG_INFINITY.signum(), -1f32); - assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); - assert!(f32::NAN.signum().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f32::INFINITY.is_sign_positive()); - assert!(1f32.is_sign_positive()); - assert!(0f32.is_sign_positive()); - assert!(!(-0f32).is_sign_positive()); - assert!(!(-1f32).is_sign_positive()); - assert!(!f32::NEG_INFINITY.is_sign_positive()); - assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); - assert!(f32::NAN.is_sign_positive()); - assert!(!(-f32::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f32::INFINITY.is_sign_negative()); - assert!(!1f32.is_sign_negative()); - assert!(!0f32.is_sign_negative()); - assert!((-0f32).is_sign_negative()); - assert!((-1f32).is_sign_negative()); - assert!(f32::NEG_INFINITY.is_sign_negative()); - assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); - assert!(!f32::NAN.is_sign_negative()); - assert!((-f32::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); - assert_f32_biteq!(f32::MIN.next_up(), -max_down); - assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); - assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f32_biteq!((-tiny_up).next_up(), -tiny); - assert_f32_biteq!((-tiny).next_up(), -0.0f32); - assert_f32_biteq!((-0.0f32).next_up(), tiny); - assert_f32_biteq!(0.0f32.next_up(), tiny); - assert_f32_biteq!(tiny.next_up(), tiny_up); - assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); - assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); - assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_up(), nan0); - assert_f32_biteq!(nan1.next_up(), nan1); - assert_f32_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!((-max_down).next_down(), f32::MIN); - assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); - assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f32_biteq!((-tiny).next_down(), -tiny_up); - assert_f32_biteq!((-0.0f32).next_down(), -tiny); - assert_f32_biteq!((0.0f32).next_down(), -tiny); - assert_f32_biteq!(tiny.next_down(), 0.0f32); - assert_f32_biteq!(tiny_up.next_down(), tiny); - assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); - assert_f32_biteq!(f32::MAX.next_down(), max_down); - assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_down(), nan0); - assert_f32_biteq!(nan1.next_down(), nan1); - assert_f32_biteq!(nan2.next_down(), nan2); -} - -#[test] -fn test_mul_add() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f32.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -fn test_recip() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.recip(), 1.0); - assert_eq!(2.0f32.recip(), 0.5); - assert_eq!((-0.4f32).recip(), -2.5); - assert_eq!(0.0f32.recip(), inf); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -#[test] -fn test_powi() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powi(1), 1.0); - assert_approx_eq!((-3.1f32).powi(2), 9.61); - assert_approx_eq!(5.9f32.powi(-2), 0.028727); - assert_eq!(8.3f32.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - #[test] fn test_powf() { let nan: f32 = f32::NAN; @@ -469,17 +27,6 @@ fn test_powf() { assert_eq!(neg_inf.powf(3.0), neg_inf); } -#[test] -fn test_sqrt_domain() { - assert!(f32::NAN.sqrt().is_nan()); - assert!(f32::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f32).sqrt().is_nan()); - assert_eq!((-0.0f32).sqrt(), -0.0); - assert_eq!(0.0f32.sqrt(), 0.0); - assert_eq!(1.0f32.sqrt(), 1.0); - assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); -} - #[test] fn test_exp() { assert_eq!(1.0, 0.0f32.exp()); @@ -573,36 +120,6 @@ fn test_log10() { assert_eq!(0.0f32.log10(), neg_inf); } -#[test] -fn test_to_degrees() { - let pi: f32 = consts::PI; - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_degrees(), 0.0); - assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f32 = consts::PI; - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_radians(), 0.0); - assert_approx_eq!(154.6f32.to_radians(), 2.698279); - assert_approx_eq!((-332.31f32).to_radians(), -5.799903); - assert_eq!(180.0f32.to_radians(), pi); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - #[test] fn test_asinh() { assert_eq!(0.0f32.asinh(), 0.0f32); @@ -734,207 +251,3 @@ fn test_real_consts() { assert_approx_eq!(ln_2, 2f32.ln()); assert_approx_eq!(ln_10, 10f32.ln()); } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f32).to_bits(), 0x3f800000); - assert_eq!((12.5f32).to_bits(), 0x41480000); - assert_eq!((1337f32).to_bits(), 0x44a72000); - assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); - assert_approx_eq!(f32::from_bits(0x41480000), 12.5); - assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); - assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; - assert!(f32::from_bits(masked_nan1).is_nan()); - assert!(f32::from_bits(masked_nan2).is_nan()); - - assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f32.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f32.clamp(f32::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f32.clamp(3.0, f32::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u32 { - 1 << (f32::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f32 { - f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) - } - - fn max_subnorm() -> f32 { - f32::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f32 { - f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f32 { - f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - -#[test] -fn test_algebraic() { - let a: f32 = 123.0; - let b: f32 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps_add = if cfg!(miri) { 1e-3 } else { 0.0 }; - let eps_mul = if cfg!(miri) { 1e-1 } else { 0.0 }; - let eps_div = if cfg!(miri) { 1e-4 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); - assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); -} diff --git a/library/std/tests/floats/f64.rs b/library/std/tests/floats/f64.rs index de9c27eb33d3..2d8dd1cf0915 100644 --- a/library/std/tests/floats/f64.rs +++ b/library/std/tests/floats/f64.rs @@ -1,26 +1,4 @@ use std::f64::consts; -use std::num::FpCategory as Fp; - -/// Smallest number -const TINY_BITS: u64 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u64 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; - -/// First pattern over the mantissa -const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u64 = 0x0005_5555_5555_5555; #[allow(unused_macros)] macro_rules! assert_f64_biteq { @@ -33,411 +11,6 @@ macro_rules! assert_f64_biteq { }; } -#[test] -fn test_num_f64() { - crate::test_num(10f64, 2f64); -} - -#[test] -fn test_min_nan() { - assert_eq!(f64::NAN.min(2.0), 2.0); - assert_eq!(2.0f64.min(f64::NAN), 2.0); -} - -#[test] -fn test_max_nan() { - assert_eq!(f64::NAN.max(2.0), 2.0); - assert_eq!(2.0f64.max(f64::NAN), 2.0); -} - -#[test] -fn test_nan() { - let nan: f64 = f64::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f64::MANTISSA_DIGITS - 2)) != 0); -} - -#[test] -fn test_infinity() { - let inf: f64 = f64::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f64 = 0.0f64; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f64 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f64 = 1.0f64; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f64.is_nan()); - assert!(!5.3f64.is_nan()); - assert!(!(-10.732f64).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f64.is_infinite()); - assert!(!42.8f64.is_infinite()); - assert!(!(-109.2f64).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f64.is_finite()); - assert!(42.8f64.is_finite()); - assert!((-109.2f64).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let zero: f64 = 0.0f64; - let neg_zero: f64 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f64.is_normal()); - assert!(1e-307f64.is_normal()); - assert!(!1e-308f64.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let zero: f64 = 0.0f64; - let neg_zero: f64 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1e-307f64.classify(), Fp::Normal); - assert_eq!(1e-308f64.classify(), Fp::Subnormal); -} - -#[test] -fn test_floor() { - assert_approx_eq!(1.0f64.floor(), 1.0f64); - assert_approx_eq!(1.3f64.floor(), 1.0f64); - assert_approx_eq!(1.5f64.floor(), 1.0f64); - assert_approx_eq!(1.7f64.floor(), 1.0f64); - assert_approx_eq!(0.0f64.floor(), 0.0f64); - assert_approx_eq!((-0.0f64).floor(), -0.0f64); - assert_approx_eq!((-1.0f64).floor(), -1.0f64); - assert_approx_eq!((-1.3f64).floor(), -2.0f64); - assert_approx_eq!((-1.5f64).floor(), -2.0f64); - assert_approx_eq!((-1.7f64).floor(), -2.0f64); -} - -#[test] -fn test_ceil() { - assert_approx_eq!(1.0f64.ceil(), 1.0f64); - assert_approx_eq!(1.3f64.ceil(), 2.0f64); - assert_approx_eq!(1.5f64.ceil(), 2.0f64); - assert_approx_eq!(1.7f64.ceil(), 2.0f64); - assert_approx_eq!(0.0f64.ceil(), 0.0f64); - assert_approx_eq!((-0.0f64).ceil(), -0.0f64); - assert_approx_eq!((-1.0f64).ceil(), -1.0f64); - assert_approx_eq!((-1.3f64).ceil(), -1.0f64); - assert_approx_eq!((-1.5f64).ceil(), -1.0f64); - assert_approx_eq!((-1.7f64).ceil(), -1.0f64); -} - -#[test] -fn test_round() { - assert_approx_eq!(2.5f64.round(), 3.0f64); - assert_approx_eq!(1.0f64.round(), 1.0f64); - assert_approx_eq!(1.3f64.round(), 1.0f64); - assert_approx_eq!(1.5f64.round(), 2.0f64); - assert_approx_eq!(1.7f64.round(), 2.0f64); - assert_approx_eq!(0.0f64.round(), 0.0f64); - assert_approx_eq!((-0.0f64).round(), -0.0f64); - assert_approx_eq!((-1.0f64).round(), -1.0f64); - assert_approx_eq!((-1.3f64).round(), -1.0f64); - assert_approx_eq!((-1.5f64).round(), -2.0f64); - assert_approx_eq!((-1.7f64).round(), -2.0f64); -} - -#[test] -fn test_round_ties_even() { - assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64); - assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64); - assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64); - assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64); - assert_approx_eq!((-1.7f64).round_ties_even(), -2.0f64); -} - -#[test] -fn test_trunc() { - assert_approx_eq!(1.0f64.trunc(), 1.0f64); - assert_approx_eq!(1.3f64.trunc(), 1.0f64); - assert_approx_eq!(1.5f64.trunc(), 1.0f64); - assert_approx_eq!(1.7f64.trunc(), 1.0f64); - assert_approx_eq!(0.0f64.trunc(), 0.0f64); - assert_approx_eq!((-0.0f64).trunc(), -0.0f64); - assert_approx_eq!((-1.0f64).trunc(), -1.0f64); - assert_approx_eq!((-1.3f64).trunc(), -1.0f64); - assert_approx_eq!((-1.5f64).trunc(), -1.0f64); - assert_approx_eq!((-1.7f64).trunc(), -1.0f64); -} - -#[test] -fn test_fract() { - assert_approx_eq!(1.0f64.fract(), 0.0f64); - assert_approx_eq!(1.3f64.fract(), 0.3f64); - assert_approx_eq!(1.5f64.fract(), 0.5f64); - assert_approx_eq!(1.7f64.fract(), 0.7f64); - assert_approx_eq!(0.0f64.fract(), 0.0f64); - assert_approx_eq!((-0.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.3f64).fract(), -0.3f64); - assert_approx_eq!((-1.5f64).fract(), -0.5f64); - assert_approx_eq!((-1.7f64).fract(), -0.7f64); -} - -#[test] -fn test_abs() { - assert_eq!(f64::INFINITY.abs(), f64::INFINITY); - assert_eq!(1f64.abs(), 1f64); - assert_eq!(0f64.abs(), 0f64); - assert_eq!((-0f64).abs(), 0f64); - assert_eq!((-1f64).abs(), 1f64); - assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); - assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); - assert!(f64::NAN.abs().is_nan()); -} - -#[test] -fn test_signum() { - assert_eq!(f64::INFINITY.signum(), 1f64); - assert_eq!(1f64.signum(), 1f64); - assert_eq!(0f64.signum(), 1f64); - assert_eq!((-0f64).signum(), -1f64); - assert_eq!((-1f64).signum(), -1f64); - assert_eq!(f64::NEG_INFINITY.signum(), -1f64); - assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); - assert!(f64::NAN.signum().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f64::INFINITY.is_sign_positive()); - assert!(1f64.is_sign_positive()); - assert!(0f64.is_sign_positive()); - assert!(!(-0f64).is_sign_positive()); - assert!(!(-1f64).is_sign_positive()); - assert!(!f64::NEG_INFINITY.is_sign_positive()); - assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); - assert!(f64::NAN.is_sign_positive()); - assert!(!(-f64::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f64::INFINITY.is_sign_negative()); - assert!(!1f64.is_sign_negative()); - assert!(!0f64.is_sign_negative()); - assert!((-0f64).is_sign_negative()); - assert!((-1f64).is_sign_negative()); - assert!(f64::NEG_INFINITY.is_sign_negative()); - assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); - assert!(!f64::NAN.is_sign_negative()); - assert!((-f64::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); - assert_f64_biteq!(f64::MIN.next_up(), -max_down); - assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); - assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f64_biteq!((-tiny_up).next_up(), -tiny); - assert_f64_biteq!((-tiny).next_up(), -0.0f64); - assert_f64_biteq!((-0.0f64).next_up(), tiny); - assert_f64_biteq!(0.0f64.next_up(), tiny); - assert_f64_biteq!(tiny.next_up(), tiny_up); - assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); - assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); - assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_up(), nan0); - assert_f64_biteq!(nan1.next_up(), nan1); - assert_f64_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!((-max_down).next_down(), f64::MIN); - assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); - assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f64_biteq!((-tiny).next_down(), -tiny_up); - assert_f64_biteq!((-0.0f64).next_down(), -tiny); - assert_f64_biteq!((0.0f64).next_down(), -tiny); - assert_f64_biteq!(tiny.next_down(), 0.0f64); - assert_f64_biteq!(tiny_up.next_down(), tiny); - assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); - assert_f64_biteq!(f64::MAX.next_down(), max_down); - assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_down(), nan0); - assert_f64_biteq!(nan1.next_down(), nan1); - assert_f64_biteq!(nan2.next_down(), nan2); -} - -#[test] -fn test_mul_add() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f64.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -fn test_recip() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.recip(), 1.0); - assert_eq!(2.0f64.recip(), 0.5); - assert_eq!((-0.4f64).recip(), -2.5); - assert_eq!(0.0f64.recip(), inf); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -#[test] -fn test_powi() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powi(1), 1.0); - assert_approx_eq!((-3.1f64).powi(2), 9.61); - assert_approx_eq!(5.9f64.powi(-2), 0.028727); - assert_eq!(8.3f64.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - #[test] fn test_powf() { let nan: f64 = f64::NAN; @@ -454,17 +27,6 @@ fn test_powf() { assert_eq!(neg_inf.powf(3.0), neg_inf); } -#[test] -fn test_sqrt_domain() { - assert!(f64::NAN.sqrt().is_nan()); - assert!(f64::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f64).sqrt().is_nan()); - assert_eq!((-0.0f64).sqrt(), -0.0); - assert_eq!(0.0f64.sqrt(), 0.0); - assert_eq!(1.0f64.sqrt(), 1.0); - assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); -} - #[test] fn test_exp() { assert_eq!(1.0, 0.0f64.exp()); @@ -558,35 +120,6 @@ fn test_log10() { assert_eq!(0.0f64.log10(), neg_inf); } -#[test] -fn test_to_degrees() { - let pi: f64 = consts::PI; - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_degrees(), 0.0); - assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); -} - -#[test] -fn test_to_radians() { - let pi: f64 = consts::PI; - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_radians(), 0.0); - assert_approx_eq!(154.6f64.to_radians(), 2.698279); - assert_approx_eq!((-332.31f64).to_radians(), -5.799903); - assert_eq!(180.0f64.to_radians(), pi); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - #[test] fn test_asinh() { assert_eq!(0.0f64.asinh(), 0.0f64); @@ -714,204 +247,3 @@ fn test_real_consts() { assert_approx_eq!(ln_2, 2f64.ln()); assert_approx_eq!(ln_10, 10f64.ln()); } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f64).to_bits(), 0x3ff0000000000000); - assert_eq!((12.5f64).to_bits(), 0x4029000000000000); - assert_eq!((1337f64).to_bits(), 0x4094e40000000000); - assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); - assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); - assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); - assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); - assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; - assert!(f64::from_bits(masked_nan1).is_nan()); - assert!(f64::from_bits(masked_nan2).is_nan()); - - assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f64.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f64.clamp(f64::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f64.clamp(3.0, f64::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u64 { - 1 << (f64::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f64 { - f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) - } - - fn max_subnorm() -> f64 { - f64::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f64 { - f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f64 { - f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - -#[test] -fn test_algebraic() { - let a: f64 = 123.0; - let b: f64 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps); - assert_approx_eq!(a.algebraic_div(b), a / b, eps); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps); -} diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs index 453a2d533ab8..8bb8eb4bfc1a 100644 --- a/library/std/tests/floats/lib.rs +++ b/library/std/tests/floats/lib.rs @@ -1,5 +1,4 @@ -#![feature(f16, f128, float_algebraic, float_gamma, float_minimum_maximum)] -#![feature(cfg_target_has_reliable_f16_f128)] +#![feature(f16, f128, float_gamma, float_minimum_maximum, cfg_target_has_reliable_f16_f128)] #![expect(internal_features)] // for reliable_f16_f128 use std::fmt; diff --git a/library/stdarch b/library/stdarch index f1c1839c0deb..1dfaa4db2479 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit f1c1839c0deb985a9f98cbd6b38a6d43f2df6157 +Subproject commit 1dfaa4db2479753a46a3e90f2c3c89d89d0b21f1 diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 05ab1b6eddef..d10d2d9bf8ce 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -64,7 +64,7 @@ dependencies = [ "tracing-subscriber", "tracing-tree", "walkdir", - "windows 0.57.0", + "windows", "xz2", ] @@ -579,9 +579,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" -version = "1.0.2" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -703,7 +703,7 @@ dependencies = [ "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -916,16 +916,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets", -] - [[package]] name = "windows" version = "0.61.1" @@ -933,7 +923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -945,19 +935,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets", + "windows-core", ] [[package]] @@ -966,10 +944,10 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link", - "windows-result 0.3.2", + "windows-result", "windows-strings", ] @@ -979,21 +957,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -1005,17 +972,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.1" @@ -1039,19 +995,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-result" version = "0.3.2" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e34de924cc18..9652d18f1a6c 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -70,7 +70,7 @@ tracing-tree = { version = "0.4.0", optional = true } version = "1.0.0" [target.'cfg(windows)'.dependencies.windows] -version = "0.57" +version = "0.61" features = [ "Win32_Foundation", "Win32_Security", diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 85c682a46c5d..374884d8a9a0 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -342,7 +342,7 @@ fn format_rusage_data(child: Child) -> Option { use windows::Win32::System::Threading::GetProcessTimes; use windows::Win32::System::Time::FileTimeToSystemTime; - let handle = HANDLE(child.as_raw_handle() as isize); + let handle = HANDLE(child.as_raw_handle()); let mut user_filetime = Default::default(); let mut user_time = Default::default(); diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 93900a9043e7..1c317ce4b86b 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -58,7 +58,7 @@ fn rustfmt( fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); - let mut cmd = command(build.initial_rustfmt()?); + let mut cmd = command(build.config.initial_rustfmt.as_ref()?); cmd.arg("--version"); let output = cmd.allow_failure().run_capture(build); @@ -243,7 +243,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { let override_ = override_builder.build().unwrap(); // `override` is a reserved keyword - let rustfmt_path = build.initial_rustfmt().unwrap_or_else(|| { + let rustfmt_path = build.config.initial_rustfmt.clone().unwrap_or_else(|| { eprintln!("fmt error: `x fmt` is not supported on this channel"); crate::exit!(1); }); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f708b6e9fd69..27791825aa0f 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1102,7 +1102,7 @@ impl Step for Tidy { if builder.config.channel == "dev" || builder.config.channel == "nightly" { if !builder.config.json_output { builder.info("fmt check"); - if builder.initial_rustfmt().is_none() { + if builder.config.initial_rustfmt.is_none() { let inferred_rustfmt_dir = builder.initial_sysroot.join("bin"); eprintln!( "\ @@ -3554,7 +3554,7 @@ impl Step for TestFloatParse { builder.ensure(tool::TestFloatParse { host: self.host }); // Run any unit tests in the crate - let cargo_test = tool::prepare_tool_cargo( + let mut cargo_test = tool::prepare_tool_cargo( builder, compiler, Mode::ToolStd, @@ -3564,6 +3564,7 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES); run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder); @@ -3578,6 +3579,7 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES); if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) { cargo_run.args(["--", "--skip-huge"]); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index ac568eab2e8a..678aa9b01e4a 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1259,6 +1259,10 @@ pub struct TestFloatParse { pub host: TargetSelection, } +impl TestFloatParse { + pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128"; +} + impl Step for TestFloatParse { type Output = ToolBuildResult; const ONLY_HOSTS: bool = true; @@ -1280,7 +1284,7 @@ impl Step for TestFloatParse { path: "src/etc/test-float-parse", source_type: SourceType::InTree, extra_features: Vec::new(), - allow_features: "", + allow_features: Self::ALLOW_FEATURES, cargo_args: Vec::new(), artifact_kind: ToolArtifactKind::Binary, }) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index cc4fa953ddc5..af3e3cc37b92 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1388,7 +1388,7 @@ impl<'a> Builder<'a> { // Windows doesn't need dylib path munging because the dlls for the // compiler live next to the compiler and the system will find them // automatically. - if cfg!(windows) { + if cfg!(any(windows, target_os = "cygwin")) { return; } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c8beca25bccc..3b8c3655b8d1 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -3,7 +3,7 @@ //! This module implements parsing `bootstrap.toml` configuration files to tweak //! how the build runs. -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::{self, Display}; use std::hash::Hash; @@ -406,11 +406,7 @@ pub struct Config { pub initial_rustc: PathBuf, pub initial_cargo_clippy: Option, pub initial_sysroot: PathBuf, - - #[cfg(not(test))] - initial_rustfmt: RefCell, - #[cfg(test)] - pub initial_rustfmt: RefCell, + pub initial_rustfmt: Option, /// The paths to work with. For example: with `./x check foo bar` we get /// `paths=["foo", "bar"]`. @@ -428,15 +424,6 @@ pub struct Config { pub path_modification_cache: Arc, PathFreshness>>>, } -#[derive(Clone, Debug, Default)] -pub enum RustfmtState { - SystemToolchain(PathBuf), - Downloaded(PathBuf), - Unavailable, - #[default] - LazyEvaluated, -} - #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum LlvmLibunwind { #[default] @@ -2448,13 +2435,8 @@ impl Config { }); } - if let Some(r) = rustfmt { - *config.initial_rustfmt.borrow_mut() = if r.exists() { - RustfmtState::SystemToolchain(r) - } else { - RustfmtState::Unavailable - }; - } + config.initial_rustfmt = + if let Some(r) = rustfmt { Some(r) } else { config.maybe_download_rustfmt() }; // Now that we've reached the end of our configuration, infer the // default values for all options that we haven't otherwise stored yet. @@ -2851,25 +2833,6 @@ impl Config { .as_deref() } - pub(crate) fn initial_rustfmt(&self) -> Option { - match &mut *self.initial_rustfmt.borrow_mut() { - RustfmtState::SystemToolchain(p) | RustfmtState::Downloaded(p) => Some(p.clone()), - RustfmtState::Unavailable => None, - r @ RustfmtState::LazyEvaluated => { - if self.dry_run() { - return Some(PathBuf::new()); - } - let path = self.maybe_download_rustfmt(); - *r = if let Some(p) = &path { - RustfmtState::Downloaded(p.clone()) - } else { - RustfmtState::Unavailable - }; - path - } - } - } - /// Runs a function if verbosity is greater than 0 pub fn verbose(&self, f: impl Fn()) { if self.is_verbose() { diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 64298964dad5..e0c9877cd55d 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -446,7 +446,7 @@ impl Config { #[cfg(test)] pub(crate) fn maybe_download_rustfmt(&self) -> Option { - None + Some(PathBuf::new()) } /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't @@ -455,6 +455,10 @@ impl Config { pub(crate) fn maybe_download_rustfmt(&self) -> Option { use build_helper::stage0_parser::VersionMetadata; + if self.dry_run() { + return Some(PathBuf::new()); + } + let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?; let channel = format!("{version}-{date}"); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 1e6acad5c0fc..9492ffaed756 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -325,7 +325,6 @@ forward! { tempdir() -> PathBuf, llvm_link_shared() -> bool, download_rustc() -> bool, - initial_rustfmt() -> Option, } impl Build { @@ -614,10 +613,6 @@ impl Build { crate::utils::job::setup(self); } - // Download rustfmt early so that it can be used in rust-analyzer configs. - trace!("downloading rustfmt early"); - let _ = &builder::Builder::new(self).initial_rustfmt(); - // Handle hard-coded subcommands. { #[cfg(feature = "tracing")] diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index b29c1fb3889c..f2c3e8c0df46 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -130,7 +130,7 @@ pub fn is_debug_info(name: &str) -> bool { /// Returns the corresponding relative library directory that the compiler's /// dylibs will be found in. pub fn libdir(target: TargetSelection) -> &'static str { - if target.is_windows() { "bin" } else { "lib" } + if target.is_windows() || target.contains("cygwin") { "bin" } else { "lib" } } /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index 4949518de79b..887deb41ca8b 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -66,7 +66,6 @@ mod for_windows { // Enable the Windows Error Reporting dialog which msys disables, // so we can JIT debug rustc let mode = SetErrorMode(THREAD_ERROR_MODE::default()); - let mode = THREAD_ERROR_MODE(mode); SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX); // Create a new job object for us to use diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index 1297a53d4880..08e1c21e58e7 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -20,7 +20,7 @@ use std::str::FromStr; /// Returns the environment variable which the dynamic library lookup path /// resides in for this platform. pub fn dylib_path_var() -> &'static str { - if cfg!(target_os = "windows") { + if cfg!(any(target_os = "windows", target_os = "cygwin")) { "PATH" } else if cfg!(target_vendor = "apple") { "DYLD_LIBRARY_PATH" diff --git a/src/ci/cpu-usage-over-time.py b/src/ci/cpu-usage-over-time.py index 3d9dc86734fc..19a39f349532 100755 --- a/src/ci/cpu-usage-over-time.py +++ b/src/ci/cpu-usage-over-time.py @@ -170,7 +170,7 @@ print("Time,Idle") while True: time.sleep(1) next_state = State() - now = datetime.datetime.utcnow().isoformat() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None).isoformat() idle = next_state.idle_since(cur_state) print("%s,%s" % (now, idle)) sys.stdout.flush() diff --git a/src/ci/docker/host-x86_64/dist-ohos/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile similarity index 56% rename from src/ci/docker/host-x86_64/dist-ohos/Dockerfile rename to src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile index 2c514fa0d4dd..adeaf809f420 100644 --- a/src/ci/docker/host-x86_64/dist-ohos/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile @@ -27,36 +27,18 @@ RUN sh /scripts/ohos-openssl.sh COPY scripts/ohos/aarch64-unknown-linux-ohos-clang.sh /usr/local/bin/ COPY scripts/ohos/aarch64-unknown-linux-ohos-clang++.sh /usr/local/bin/ -COPY scripts/ohos/armv7-unknown-linux-ohos-clang.sh /usr/local/bin/ -COPY scripts/ohos/armv7-unknown-linux-ohos-clang++.sh /usr/local/bin/ -COPY scripts/ohos/x86_64-unknown-linux-ohos-clang.sh /usr/local/bin/ -COPY scripts/ohos/x86_64-unknown-linux-ohos-clang++.sh /usr/local/bin/ # env ENV AARCH64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/arm64-v8a -ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/armeabi-v7a -ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/x86_64 ENV AARCH64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 -ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 -ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 ENV TARGETS=aarch64-unknown-linux-ohos -ENV TARGETS=$TARGETS,armv7-unknown-linux-ohos -ENV TARGETS=$TARGETS,x86_64-unknown-linux-ohos ENV \ CC_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang.sh \ AR_aarch64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ CXX_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang++.sh -ENV \ - CC_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang.sh \ - AR_armv7_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ - CXX_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang++.sh -ENV \ - CC_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang.sh \ - AR_x86_64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ - CXX_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang++.sh ENV RUST_CONFIGURE_ARGS \ --enable-profiler \ diff --git a/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile new file mode 100644 index 000000000000..2a23d8aec237 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/ohos-sdk.sh /scripts/ +RUN sh /scripts/ohos-sdk.sh + +COPY scripts/ohos-openssl.sh /scripts/ +RUN sh /scripts/ohos-openssl.sh + +COPY scripts/ohos/armv7-unknown-linux-ohos-clang.sh /usr/local/bin/ +COPY scripts/ohos/armv7-unknown-linux-ohos-clang++.sh /usr/local/bin/ + +# env +ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/armeabi-v7a + +ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 + +ENV TARGETS=armv7-unknown-linux-ohos + +ENV \ + CC_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang.sh \ + AR_armv7_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ + CXX_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang++.sh + +ENV RUST_CONFIGURE_ARGS \ + --enable-profiler \ + --disable-docs \ + --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ + --enable-extended \ + --enable-sanitizers + +ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile new file mode 100644 index 000000000000..98e402adf2a6 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/ohos-sdk.sh /scripts/ +RUN sh /scripts/ohos-sdk.sh + +COPY scripts/ohos-openssl.sh /scripts/ +RUN sh /scripts/ohos-openssl.sh + +COPY scripts/ohos/x86_64-unknown-linux-ohos-clang.sh /usr/local/bin/ +COPY scripts/ohos/x86_64-unknown-linux-ohos-clang++.sh /usr/local/bin/ + +# env +ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/x86_64 + +ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 + +ENV TARGETS=x86_64-unknown-linux-ohos + +ENV \ + CC_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang.sh \ + AR_x86_64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ + CXX_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang++.sh + +ENV RUST_CONFIGURE_ARGS \ + --enable-profiler \ + --disable-docs \ + --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ + --enable-extended \ + --enable-sanitizers + +ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 9ed5b519b6ea..9222710b8437 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -47,7 +47,7 @@ fi # we only ensure that all assertions still pass. MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zmir-opt-level=4 -Cdebug-assertions=yes" \ MIRI_SKIP_UI_CHECKS=1 \ - python3 "$X_PY" test --stage 2 src/tools/miri -- tests/{pass,panic} + python3 "$X_PY" test --stage 2 src/tools/miri -- tests/pass tests/panic # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. case $HOST_TARGET in diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index afcc092e78e8..42ad5acbdac1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -35,6 +35,8 @@ runners: os: windows-2022 <<: *base-job + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. - &job-windows-25 os: windows-2025 <<: *base-job @@ -186,8 +188,14 @@ auto: - name: dist-loongarch64-musl <<: *job-linux-4c - - name: dist-ohos - <<: *job-linux-4c-largedisk + - name: dist-ohos-aarch64 + <<: *job-linux-4c + + - name: dist-ohos-armv7 + <<: *job-linux-4c + + - name: dist-ohos-x86_64 + <<: *job-linux-4c - name: dist-powerpc-linux <<: *job-linux-4c @@ -476,13 +484,17 @@ auto: env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler SCRIPT: make ci-msvc-py - <<: *job-windows-25 + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. + <<: *job-windows - name: x86_64-msvc-2 env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler SCRIPT: make ci-msvc-ps1 - <<: *job-windows-25 + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. + <<: *job-windows # i686-msvc is split into two jobs to run tests in parallel. - name: i686-msvc-1 diff --git a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md index 8e6725c54efa..55436261fdef 100644 --- a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md +++ b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md @@ -80,41 +80,11 @@ approachable and practical; it may make sense to direct users to an RFC or some other issue for the full details. The issue also serves as a place where users can comment with questions or other concerns. -A template for these breaking-change tracking issues can be found below. An -example of how such an issue should look can be [found +A template for these breaking-change tracking issues can be found +[here][template]. An example of how such an issue should look can be [found here][breaking-change-issue]. -The issue should be tagged with (at least) `B-unstable` and `T-compiler`. - -### Tracking issue template - -This is a template to use for tracking issues: - -``` -This is the **summary issue** for the `YOUR_LINT_NAME_HERE` -future-compatibility warning and other related errors. The goal of -this page is describe why this change was made and how you can fix -code that is affected by it. It also provides a place to ask questions -or register a complaint if you feel the change should not be made. For -more information on the policy around future-compatibility warnings, -see our [breaking change policy guidelines][guidelines]. - -[guidelines]: LINK_TO_THIS_RFC - -#### What is the warning for? - -*Describe the conditions that trigger the warning and how they can be -fixed. Also explain why the change was made.** - -#### When will this warning become a hard error? - -At the beginning of each 6-week release cycle, the Rust compiler team -will review the set of outstanding future compatibility warnings and -nominate some of them for **Final Comment Period**. Toward the end of -the cycle, we will review any comments and make a final determination -whether to convert the warning into a hard error or remove it -entirely. -``` +[template]: https://github.com/rust-lang/rust/issues/new?template=tracking_issue_future.md ### Issuing future compatibility warnings diff --git a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md index 9ba4eff629e7..696f2038e1a4 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md @@ -1,9 +1,9 @@ # Rust for Linux notification group -**Github Label:** [O-rfl]
+**Github Label:** [A-rust-for-linux]
**Ping command:** `@rustbot ping rfl` -[O-rfl]: https://github.com/rust-lang/rust/labels/O-rfl +[A-rust-for-linux]: https://github.com/rust-lang/rust/labels/A-rust-for-linux This list will be used to notify [Rust for Linux (RfL)][rfl] maintainers when the compiler or the standard library changes in a way that would diff --git a/src/doc/rustc/book.toml b/src/doc/rustc/book.toml index 167aece0ed6a..01f127ad3905 100644 --- a/src/doc/rustc/book.toml +++ b/src/doc/rustc/book.toml @@ -6,6 +6,8 @@ title = "The rustc book" [output.html] git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc" edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}" +additional-css = ["theme/pagetoc.css"] +additional-js = ["theme/pagetoc.js"] [output.html.search] use-boolean-and = true diff --git a/src/doc/rustc/theme/pagetoc.css b/src/doc/rustc/theme/pagetoc.css new file mode 100644 index 000000000000..58ca1f8b26f8 --- /dev/null +++ b/src/doc/rustc/theme/pagetoc.css @@ -0,0 +1,84 @@ +/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) */ + +:root { + --toc-width: 270px; + --center-content-toc-shift: calc(-1 * var(--toc-width) / 2); +} + +.nav-chapters { + /* adjust width of buttons that bring to the previous or the next page */ + min-width: 50px; +} + +@media only screen { + @media (max-width: 1179px) { + .sidebar-hidden #sidetoc { + display: none; + } + } + + @media (max-width: 1439px) { + .sidebar-visible #sidetoc { + display: none; + } + } + + @media (1180px <= width <= 1439px) { + .sidebar-hidden main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + @media (1440px <= width <= 1700px) { + .sidebar-visible main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + #sidetoc { + margin-left: calc(100% + 20px); + } + #pagetoc { + position: fixed; + /* adjust TOC width */ + width: var(--toc-width); + height: calc(100vh - var(--menu-bar-height) - 0.67em * 4); + overflow: auto; + } + #pagetoc a { + border-left: 1px solid var(--sidebar-bg); + color: var(--sidebar-fg) !important; + display: block; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 10px; + text-align: left; + text-decoration: none; + } + #pagetoc a:hover, + #pagetoc a.active { + background: var(--sidebar-bg); + color: var(--sidebar-active) !important; + } + #pagetoc .active { + background: var(--sidebar-bg); + color: var(--sidebar-active); + } + #pagetoc .pagetoc-H2 { + padding-left: 20px; + } + #pagetoc .pagetoc-H3 { + padding-left: 40px; + } + #pagetoc .pagetoc-H4 { + padding-left: 60px; + } +} + +@media print { + #sidetoc { + display: none; + } +} diff --git a/src/doc/rustc/theme/pagetoc.js b/src/doc/rustc/theme/pagetoc.js new file mode 100644 index 000000000000..927a5b10749b --- /dev/null +++ b/src/doc/rustc/theme/pagetoc.js @@ -0,0 +1,104 @@ +// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) + +let activeHref = location.href; +function updatePageToc(elem = undefined) { + let selectedPageTocElem = elem; + const pagetoc = document.getElementById("pagetoc"); + + function getRect(element) { + return element.getBoundingClientRect(); + } + + function overflowTop(container, element) { + return getRect(container).top - getRect(element).top; + } + + function overflowBottom(container, element) { + return getRect(container).bottom - getRect(element).bottom; + } + + // We've not selected a heading to highlight, and the URL needs updating + // so we need to find a heading based on the URL + if (selectedPageTocElem === undefined && location.href !== activeHref) { + activeHref = location.href; + for (const pageTocElement of pagetoc.children) { + if (pageTocElement.href === activeHref) { + selectedPageTocElem = pageTocElement; + } + } + } + + // We still don't have a selected heading, let's try and find the most + // suitable heading based on the scroll position + if (selectedPageTocElem === undefined) { + const margin = window.innerHeight / 3; + + const headers = document.getElementsByClassName("header"); + for (let i = 0; i < headers.length; i++) { + const header = headers[i]; + if (selectedPageTocElem === undefined && getRect(header).top >= 0) { + if (getRect(header).top < margin) { + selectedPageTocElem = header; + } else { + selectedPageTocElem = headers[Math.max(0, i - 1)]; + } + } + // a very long last section's heading is over the screen + if (selectedPageTocElem === undefined && i === headers.length - 1) { + selectedPageTocElem = header; + } + } + } + + // Remove the active flag from all pagetoc elements + for (const pageTocElement of pagetoc.children) { + pageTocElement.classList.remove("active"); + } + + // If we have a selected heading, set it to active and scroll to it + if (selectedPageTocElem !== undefined) { + for (const pageTocElement of pagetoc.children) { + if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) { + pageTocElement.classList.add("active"); + if (overflowTop(pagetoc, pageTocElement) > 0) { + pagetoc.scrollTop = pageTocElement.offsetTop; + } + if (overflowBottom(pagetoc, pageTocElement) < 0) { + pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement); + } + } + } + } +} + +if (document.getElementById("sidetoc") === null && + document.getElementsByClassName("header").length > 0) { + // The sidetoc element doesn't exist yet, let's create it + + // Create the empty sidetoc and pagetoc elements + const sidetoc = document.createElement("div"); + const pagetoc = document.createElement("div"); + sidetoc.id = "sidetoc"; + pagetoc.id = "pagetoc"; + sidetoc.appendChild(pagetoc); + + // And append them to the current DOM + const main = document.querySelector('main'); + main.insertBefore(sidetoc, main.firstChild); + + // Populate sidebar on load + window.addEventListener("load", () => { + for (const header of document.getElementsByClassName("header")) { + const link = document.createElement("a"); + link.innerHTML = header.innerHTML; + link.href = header.hash; + link.classList.add("pagetoc-" + header.parentElement.tagName); + document.getElementById("pagetoc").appendChild(link); + link.onclick = () => updatePageToc(link); + } + updatePageToc(); + }); + + // Update page table of contents selected heading on scroll + window.addEventListener("scroll", () => updatePageToc()); +} diff --git a/src/etc/test-float-parse/Cargo.toml b/src/etc/test-float-parse/Cargo.toml index 8a9c5322ef7b..e407e322f9e1 100644 --- a/src/etc/test-float-parse/Cargo.toml +++ b/src/etc/test-float-parse/Cargo.toml @@ -13,3 +13,10 @@ rayon = "1" [lib] name = "test_float_parse" + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # Internal features aren't marked known config by default + 'cfg(target_has_reliable_f16)', +] diff --git a/src/etc/test-float-parse/src/gen_/subnorm.rs b/src/etc/test-float-parse/src/gen_/subnorm.rs index 4fe3b90a3ddf..654f324b9b01 100644 --- a/src/etc/test-float-parse/src/gen_/subnorm.rs +++ b/src/etc/test-float-parse/src/gen_/subnorm.rs @@ -1,4 +1,3 @@ -use std::cmp::min; use std::fmt::Write; use std::ops::RangeInclusive; @@ -83,7 +82,13 @@ where } fn new() -> Self { - Self { iter: F::Int::ZERO..=min(F::Int::ONE << 22, F::MAN_BITS.try_into().unwrap()) } + let upper_lim = if F::MAN_BITS >= 22 { + F::Int::ONE << 22 + } else { + (F::Int::ONE << F::MAN_BITS) - F::Int::ONE + }; + + Self { iter: F::Int::ZERO..=upper_lim } } fn write_string(s: &mut String, ctx: Self::WriteCtx) { diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs index 3c3ef5802b6a..0bd4878f9a62 100644 --- a/src/etc/test-float-parse/src/lib.rs +++ b/src/etc/test-float-parse/src/lib.rs @@ -1,3 +1,7 @@ +#![feature(f16)] +#![feature(cfg_target_has_reliable_f16_f128)] +#![expect(internal_features)] // reliable_f16_f128 + mod traits; mod ui; mod validate; @@ -114,6 +118,9 @@ pub fn register_tests(cfg: &Config) -> Vec { let mut tests = Vec::new(); // Register normal generators for all floats. + + #[cfg(target_has_reliable_f16)] + register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs index 57e702b7d091..65a8721bfa5c 100644 --- a/src/etc/test-float-parse/src/traits.rs +++ b/src/etc/test-float-parse/src/traits.rs @@ -98,7 +98,7 @@ macro_rules! impl_int { } } -impl_int!(u32, i32; u64, i64); +impl_int!(u16, i16; u32, i32; u64, i64); /// Floating point types. pub trait Float: @@ -170,6 +170,9 @@ macro_rules! impl_float { impl_float!(f32, u32; f64, u64); +#[cfg(target_has_reliable_f16)] +impl_float!(f16, u16); + /// A test generator. Should provide an iterator that produces unique patterns to parse. /// /// The iterator needs to provide a `WriteCtx` (could be anything), which is then used to diff --git a/src/gcc b/src/gcc index 0ea98a1365b8..04ce66d8c918 160000 --- a/src/gcc +++ b/src/gcc @@ -1 +1 @@ -Subproject commit 0ea98a1365b81f7488073512c850e8ee951a4afd +Subproject commit 04ce66d8c918de9273bd7101638ad8724edf5e21 diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 1541e7201cef..439777843fb0 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -144,7 +144,7 @@ impl Cfg { /// Whether the configuration consists of just `Cfg` or `Not`. fn is_simple(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true, Cfg::All(..) | Cfg::Any(..) => false, } @@ -152,7 +152,7 @@ impl Cfg { /// Whether the configuration consists of just `Cfg`, `Not` or `All`. fn is_all(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true, Cfg::Any(..) => false, } @@ -204,7 +204,7 @@ impl Cfg { } fn should_append_only_to_description(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True => false, Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true, Cfg::Not(box Cfg::Cfg(..)) => true, @@ -261,17 +261,17 @@ impl ops::Not for Cfg { impl ops::BitAndAssign for Cfg { fn bitand_assign(&mut self, other: Cfg) { match (self, other) { - (&mut Cfg::False, _) | (_, Cfg::True) => {} + (Cfg::False, _) | (_, Cfg::True) => {} (s, Cfg::False) => *s = Cfg::False, - (s @ &mut Cfg::True, b) => *s = b, - (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => { + (s @ Cfg::True, b) => *s = b, + (Cfg::All(a), Cfg::All(ref mut b)) => { for c in b.drain(..) { if !a.contains(&c) { a.push(c); } } } - (&mut Cfg::All(ref mut a), ref mut b) => { + (Cfg::All(a), ref mut b) => { if !a.contains(b) { a.push(mem::replace(b, Cfg::True)); } @@ -305,15 +305,15 @@ impl ops::BitOrAssign for Cfg { fn bitor_assign(&mut self, other: Cfg) { match (self, other) { (Cfg::True, _) | (_, Cfg::False) | (_, Cfg::True) => {} - (s @ &mut Cfg::False, b) => *s = b, - (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => { + (s @ Cfg::False, b) => *s = b, + (Cfg::Any(a), Cfg::Any(ref mut b)) => { for c in b.drain(..) { if !a.contains(&c) { a.push(c); } } } - (&mut Cfg::Any(ref mut a), ref mut b) => { + (Cfg::Any(a), ref mut b) => { if !a.contains(b) { a.push(mem::replace(b, Cfg::True)); } @@ -440,40 +440,34 @@ impl Display<'_> { impl fmt::Display for Display<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self.0 { - Cfg::Not(ref child) => match **child { - Cfg::Any(ref sub_cfgs) => { - let separator = - if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; - fmt.write_str("neither ")?; + match self.0 { + Cfg::Not(box Cfg::Any(sub_cfgs)) => { + let separator = + if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; + fmt.write_str("neither ")?; - sub_cfgs - .iter() - .map(|sub_cfg| { - fmt::from_fn(|fmt| { - write_with_opt_paren( - fmt, - !sub_cfg.is_all(), - Display(sub_cfg, self.1), - ) - }) + sub_cfgs + .iter() + .map(|sub_cfg| { + fmt::from_fn(|fmt| { + write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1)) }) - .joined(separator, fmt) - } - ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)), - ref c => write!(fmt, "not ({})", Display(c, self.1)), - }, + }) + .joined(separator, fmt) + } + Cfg::Not(box simple @ Cfg::Cfg(..)) => write!(fmt, "non-{}", Display(simple, self.1)), + Cfg::Not(box c) => write!(fmt, "not ({})", Display(c, self.1)), - Cfg::Any(ref sub_cfgs) => { + Cfg::Any(sub_cfgs) => { let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " }; self.display_sub_cfgs(fmt, sub_cfgs, separator) } - Cfg::All(ref sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "), + Cfg::All(sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "), Cfg::True => fmt.write_str("everywhere"), Cfg::False => fmt.write_str("nowhere"), - Cfg::Cfg(name, value) => { + &Cfg::Cfg(name, value) => { let human_readable = match (name, value) { (sym::unix, None) => "Unix", (sym::windows, None) => "Windows", diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 622a410837b5..28dfa01534ea 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -224,9 +224,9 @@ fn clean_generic_bound<'tcx>( bound: &hir::GenericBound<'tcx>, cx: &mut DocContext<'tcx>, ) -> Option { - Some(match *bound { + Some(match bound { hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), - hir::GenericBound::Trait(ref t) => { + hir::GenericBound::Trait(t) => { // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. if let hir::BoundConstness::Maybe(_) = t.modifiers.constness && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap()) @@ -352,8 +352,8 @@ fn clean_where_predicate<'tcx>( if !predicate.kind.in_where_clause() { return None; } - Some(match *predicate.kind { - hir::WherePredicateKind::BoundPredicate(ref wbp) => { + Some(match predicate.kind { + hir::WherePredicateKind::BoundPredicate(wbp) => { let bound_params = wbp .bound_generic_params .iter() @@ -366,12 +366,12 @@ fn clean_where_predicate<'tcx>( } } - hir::WherePredicateKind::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { + hir::WherePredicateKind::RegionPredicate(wrp) => WherePredicate::RegionPredicate { lifetime: clean_lifetime(wrp.lifetime, cx), bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }, - hir::WherePredicateKind::EqPredicate(ref wrp) => WherePredicate::EqPredicate { + hir::WherePredicateKind::EqPredicate(wrp) => WherePredicate::EqPredicate { lhs: clean_ty(wrp.lhs_ty, cx), rhs: clean_ty(wrp.rhs_ty, cx).into(), }, @@ -2112,7 +2112,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ); Type::Path { path } } - ty::Dynamic(obj, ref reg, _) => { + ty::Dynamic(obj, reg, _) => { // HACK: pick the first `did` as the `did` of the trait object. Someone // might want to implement "native" support for marker-trait-only // trait objects. @@ -2129,7 +2129,7 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); - let lifetime = clean_trait_object_lifetime_bound(*reg, container, obj, cx.tcx); + let lifetime = clean_trait_object_lifetime_bound(reg, container, obj, cx.tcx); let mut bounds = dids .map(|did| { @@ -2846,7 +2846,7 @@ fn clean_maybe_renamed_item<'tcx>( )); return ret; } - ItemKind::Enum(_, ref def, generics) => EnumItem(Enum { + ItemKind::Enum(_, def, generics) => EnumItem(Enum { variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), generics: clean_generics(generics, cx), }), @@ -2854,11 +2854,11 @@ fn clean_maybe_renamed_item<'tcx>( generics: clean_generics(generics, cx), bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }), - ItemKind::Union(_, ref variant_data, generics) => UnionItem(Union { + ItemKind::Union(_, variant_data, generics) => UnionItem(Union { generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Struct(_, ref variant_data, generics) => StructItem(Struct { + ItemKind::Struct(_, variant_data, generics) => StructItem(Struct { ctor_kind: variant_data.ctor_kind(), generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 15890fff0c33..75f1bc9549cb 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1337,9 +1337,9 @@ pub(crate) enum WherePredicate { impl WherePredicate { pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> { - match *self { - WherePredicate::BoundPredicate { ref bounds, .. } => Some(bounds), - WherePredicate::RegionPredicate { ref bounds, .. } => Some(bounds), + match self { + WherePredicate::BoundPredicate { bounds, .. } => Some(bounds), + WherePredicate::RegionPredicate { bounds, .. } => Some(bounds), _ => None, } } @@ -1709,13 +1709,13 @@ impl Type { /// /// [clean]: crate::clean pub(crate) fn def_id(&self, cache: &Cache) -> Option { - let t: PrimitiveType = match *self { - Type::Path { ref path } => return Some(path.def_id()), - DynTrait(ref bounds, _) => return bounds.first().map(|b| b.trait_.def_id()), - Primitive(p) => return cache.primitive_locations.get(&p).cloned(), + let t: PrimitiveType = match self { + Type::Path { path } => return Some(path.def_id()), + DynTrait(bounds, _) => return bounds.first().map(|b| b.trait_.def_id()), + Primitive(p) => return cache.primitive_locations.get(p).cloned(), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, - BorrowedRef { ref type_, .. } => return type_.def_id(cache), - Tuple(ref tys) => { + BorrowedRef { type_, .. } => return type_.def_id(cache), + Tuple(tys) => { if tys.is_empty() { PrimitiveType::Unit } else { @@ -1727,7 +1727,7 @@ impl Type { Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, - QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache), + QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, }; Primitive(t).def_id(cache) diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index de6537e992f1..3aba7a370adb 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -70,12 +70,12 @@ impl Serialize for ItemType { impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { - let kind = match item.kind { - clean::StrippedItem(box ref item) => item, - ref kind => kind, + let kind = match &item.kind { + clean::StrippedItem(box item) => item, + kind => kind, }; - match *kind { + match kind { clean::ModuleItem(..) => ItemType::Module, clean::ExternCrateItem { .. } => ItemType::ExternCrate, clean::ImportItem(..) => ItemType::Import, @@ -103,7 +103,7 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::ForeignTypeItem => ItemType::ForeignType, clean::KeywordItem => ItemType::Keyword, clean::TraitAliasItem(..) => ItemType::TraitAlias, - clean::ProcMacroItem(ref mac) => match mac.kind { + clean::ProcMacroItem(mac) => match mac.kind { MacroKind::Bang => ItemType::Macro, MacroKind::Attr => ItemType::ProcAttribute, MacroKind::Derive => ItemType::ProcDerive, @@ -134,22 +134,15 @@ impl ItemType { DefKind::Trait => Self::Trait, DefKind::TyAlias => Self::TypeAlias, DefKind::TraitAlias => Self::TraitAlias, - DefKind::Macro(kind) => match kind { - MacroKind::Bang => ItemType::Macro, - MacroKind::Attr => ItemType::ProcAttribute, - MacroKind::Derive => ItemType::ProcDerive, - }, + DefKind::Macro(MacroKind::Bang) => ItemType::Macro, + DefKind::Macro(MacroKind::Attr) => ItemType::ProcAttribute, + DefKind::Macro(MacroKind::Derive) => ItemType::ProcDerive, DefKind::ForeignTy => Self::ForeignType, DefKind::Variant => Self::Variant, DefKind::Field => Self::StructField, DefKind::AssocTy => Self::AssocType, - DefKind::AssocFn => { - if let Some(DefKind::Trait) = parent_kind { - Self::TyMethod - } else { - Self::Method - } - } + DefKind::AssocFn if let Some(DefKind::Trait) = parent_kind => Self::TyMethod, + DefKind::AssocFn => Self::Method, DefKind::Ctor(CtorOf::Struct, _) => Self::Struct, DefKind::Ctor(CtorOf::Variant, _) => Self::Variant, DefKind::AssocConst => Self::AssocConst, @@ -170,7 +163,7 @@ impl ItemType { } pub(crate) fn as_str(&self) -> &'static str { - match *self { + match self { ItemType::Module => "mod", ItemType::ExternCrate => "externcrate", ItemType::Import => "import", @@ -199,10 +192,10 @@ impl ItemType { } } pub(crate) fn is_method(&self) -> bool { - matches!(*self, ItemType::Method | ItemType::TyMethod) + matches!(self, ItemType::Method | ItemType::TyMethod) } pub(crate) fn is_adt(&self) -> bool { - matches!(*self, ItemType::Struct | ItemType::Union | ItemType::Enum) + matches!(self, ItemType::Struct | ItemType::Union | ItemType::Enum) } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 299fd6b9adbb..8c7ab640bed3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -856,15 +856,15 @@ fn fmt_type( ) -> fmt::Result { trace!("fmt_type(t = {t:?})"); - match *t { + match t { clean::Generic(name) => f.write_str(name.as_str()), clean::SelfTy => f.write_str("Self"), - clean::Type::Path { ref path } => { + clean::Type::Path { path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx) } - clean::DynTrait(ref bounds, ref lt) => { + clean::DynTrait(bounds, lt) => { f.write_str("dyn ")?; tybounds(bounds, lt, cx).fmt(f) } @@ -872,8 +872,8 @@ fn fmt_type( clean::Primitive(clean::PrimitiveType::Never) => { primitive_link(f, PrimitiveType::Never, format_args!("!"), cx) } - clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx), - clean::BareFunction(ref decl) => { + &clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx), + clean::BareFunction(decl) => { print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?; decl.safety.print_with_space().fmt(f)?; print_abi_with_space(decl.abi).fmt(f)?; @@ -884,11 +884,11 @@ fn fmt_type( } decl.decl.print(cx).fmt(f) } - clean::UnsafeBinder(ref binder) => { + clean::UnsafeBinder(binder) => { print_higher_ranked_params_with_space(&binder.generic_params, cx, "unsafe").fmt(f)?; binder.ty.print(cx).fmt(f) } - clean::Tuple(ref typs) => match &typs[..] { + clean::Tuple(typs) => match &typs[..] { &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx), [one] => { if let clean::Generic(name) = one { @@ -925,45 +925,36 @@ fn fmt_type( } } }, - clean::Slice(ref t) => match **t { - clean::Generic(name) => { - primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) - } - _ => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - write!(f, "]") - } - }, - clean::Type::Pat(ref t, ref pat) => { + clean::Slice(box clean::Generic(name)) => { + primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) + } + clean::Slice(t) => { + write!(f, "[")?; + t.print(cx).fmt(f)?; + write!(f, "]") + } + clean::Type::Pat(t, pat) => { fmt::Display::fmt(&t.print(cx), f)?; write!(f, " is {pat}") } - clean::Array(ref t, ref n) => match **t { - clean::Generic(name) if !f.alternate() => primitive_link( - f, - PrimitiveType::Array, - format_args!("[{name}; {n}]", n = Escape(n)), - cx, - ), - _ => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - if f.alternate() { - write!(f, "; {n}")?; - } else { - write!(f, "; ")?; - primitive_link( - f, - PrimitiveType::Array, - format_args!("{n}", n = Escape(n)), - cx, - )?; - } - write!(f, "]") + clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( + f, + PrimitiveType::Array, + format_args!("[{name}; {n}]", n = Escape(n)), + cx, + ), + clean::Array(t, n) => { + write!(f, "[")?; + t.print(cx).fmt(f)?; + if f.alternate() { + write!(f, "; {n}")?; + } else { + write!(f, "; ")?; + primitive_link(f, PrimitiveType::Array, format_args!("{n}", n = Escape(n)), cx)?; } - }, - clean::RawPointer(m, ref t) => { + write!(f, "]") + } + clean::RawPointer(m, t) => { let m = match m { hir::Mutability::Mut => "mut", hir::Mutability::Not => "const", @@ -991,7 +982,7 @@ fn fmt_type( t.print(cx).fmt(f) } } - clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => { + clean::BorrowedRef { lifetime: l, mutability, type_: ty } => { let lt = fmt::from_fn(|f| match l { Some(l) => write!(f, "{} ", l.print()), _ => Ok(()), @@ -1028,11 +1019,11 @@ fn fmt_type( } Ok(()) } - clean::ImplTrait(ref bounds) => { + clean::ImplTrait(bounds) => { f.write_str("impl ")?; print_generic_bounds(bounds, cx).fmt(f) } - clean::QPath(box clean::QPathData { + &clean::QPath(box clean::QPathData { ref assoc, ref self_type, ref trait_, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b4210e7b5181..b4003044e207 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -11,7 +11,6 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(test)] diff --git a/src/llvm-project b/src/llvm-project index 8448283b4bd3..c1118fdbb302 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 8448283b4bd34ea00d76fd4f18ec730b549d6e1d +Subproject commit c1118fdbb3024157df7f4cfe765f2b0b4339e8a2 diff --git a/src/tools/cargo b/src/tools/cargo index 056f5f4f3c10..47c911e9e6f6 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 056f5f4f3c100cb36b5e9aed2d20b9ea70aae295 +Subproject commit 47c911e9e6f6461f90ce19142031fe16876a3b95 diff --git a/src/tools/clippy/.github/workflows/clippy_changelog.yml b/src/tools/clippy/.github/workflows/clippy_changelog.yml index 1e97154bf8a3..4d84d6b6dae4 100644 --- a/src/tools/clippy/.github/workflows/clippy_changelog.yml +++ b/src/tools/clippy/.github/workflows/clippy_changelog.yml @@ -15,27 +15,18 @@ jobs: changelog: runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: # Run - name: Check Changelog if: ${{ github.event_name == 'pull_request' }} run: | - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") - if [ -z "$output" ]; then - echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it." + if [[ -z $(grep -oP 'changelog: *\K\S+' <<< "$PR_BODY") ]]; then + echo "::error::Pull request message must contain 'changelog: ...' with your changelog. Please add it." exit 1 - else - echo "changelog: $output" fi env: - PYTHONIOENCODING: 'utf-8' - PR_NUMBER: '${{ github.event.number }}' + PR_BODY: ${{ github.event.pull_request.body }}) + # We need to have the "conclusion" job also on PR CI, to make it possible # to add PRs to a merge queue. diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 2b62c9a59aa5..28147dfbea3e 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,7 +6,105 @@ document. ## Unreleased / Beta / In Rust Nightly -[3e3715c3...master](https://github.com/rust-lang/rust-clippy/compare/3e3715c3...master) +[1e5237f4...master](https://github.com/rust-lang/rust-clippy/compare/1e5237f4...master) + +## Rust 1.87 + +Current stable, released 2025-05-15 + +[View all 127 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-02-06T14%3A54%3A28Z..2025-03-20T20%3A07%3A53Z+base%3Amaster) + +### New Lints + +* Added [`doc_comment_double_space_linebreaks`] to `pedantic` [#12876](https://github.com/rust-lang/rust-clippy/pull/12876) +* Added [`manual_midpoint`] to `pedantic` [#13851](https://github.com/rust-lang/rust-clippy/pull/13851) +* Added [`io_other_error`] to `style` [#14022](https://github.com/rust-lang/rust-clippy/pull/14022) +* Added [`owned_cow`] to `pedantic` [#13948](https://github.com/rust-lang/rust-clippy/pull/13948) +* Added [`manual_contains`] to `perf` [#13817](https://github.com/rust-lang/rust-clippy/pull/13817) +* Added [`unnecessary_debug_formatting`] to `pedantic` [#13893](https://github.com/rust-lang/rust-clippy/pull/13893) +* Added [`elidable_lifetime_names`] to `pedantic` [#13960](https://github.com/rust-lang/rust-clippy/pull/13960) +* Added [`mem_replace_option_with_some`] to `style` [#14197](https://github.com/rust-lang/rust-clippy/pull/14197) +* Added [`unbuffered_bytes`] to `perf` [#14089](https://github.com/rust-lang/rust-clippy/pull/14089) +* Added [`single_option_map`] to `nursery` [#14033](https://github.com/rust-lang/rust-clippy/pull/14033) + +### Moves and Deprecations + +* Moved [`comparison_chain`] to `pedantic` (from `style`) + [#14219](https://github.com/rust-lang/rust-clippy/pull/14219) +* Moved [`manual_ok_or`] to `style` (from `pedantic`) + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) +* Deprecated [`option_map_or_err_ok`] in favor of [`manual_ok_or`] + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) + +### Enhancements + +* Add `allow_expect_in_consts` and `allow_unwrap_in_consts` configuration options to [`unwrap_used`], [`expect_used`] + [#14200](https://github.com/rust-lang/rust-clippy/pull/14200) +* Add `check-incompatible-msrv-in-tests` configuration option to [`incompatible_msrv`] + [#14279](https://github.com/rust-lang/rust-clippy/pull/14279) +* [`len_zero`] now also triggers if deref target implements `is_empty()` + [#13871](https://github.com/rust-lang/rust-clippy/pull/13871) +* [`ptr_eq`] now handles more cases, including `!=` in addition to `==` + [#14339](https://github.com/rust-lang/rust-clippy/pull/14339) +* [`struct_field_names`] now also checks private fields of public structs + [#14076](https://github.com/rust-lang/rust-clippy/pull/14076) +* [`needless_pass_by_value`] suggests using a reference on the innermost `Option` content + [#14392](https://github.com/rust-lang/rust-clippy/pull/14392) +* [`obfuscated_if_else`] now supports `then().unwrap_or_else()` and `then_some().unwrap_or_else()` + [#14165](https://github.com/rust-lang/rust-clippy/pull/14165) +* Format macros: all format-handling lints now validate `todo!` and `unimplemented!` macros + [#14266](https://github.com/rust-lang/rust-clippy/pull/14266) +* [`disallowed_methods`] now supports replacements + [#13669](https://github.com/rust-lang/rust-clippy/pull/13669) +* Added MSRV checks for several lints: + * [`question_mark`] [#14436](https://github.com/rust-lang/rust-clippy/pull/14436) + * [`repeat_vec_with_capacity`] [#14126](https://github.com/rust-lang/rust-clippy/pull/14126) + * [`manual_flatten`] [#14086](https://github.com/rust-lang/rust-clippy/pull/14086) + * [`lines_filter_map_ok`] [#14130](https://github.com/rust-lang/rust-clippy/pull/14130) + +### False Positive Fixes + +* [`missing_const_for_fn`] no longer triggers on unstable const traits [#14294](https://github.com/rust-lang/rust-clippy/pull/14294) +* [`unnecessary_to_owned`] now avoids suggesting to call `iter()` on a temporary object [#14243](https://github.com/rust-lang/rust-clippy/pull/14243) +* [`unnecessary_debug_formatting`] no longer triggers in tests [#14347](https://github.com/rust-lang/rust-clippy/pull/14347) +* [`option_if_let_else`] now handles cases when value is partially moved [#14209](https://github.com/rust-lang/rust-clippy/pull/14209) +* [`blocks_in_conditions`] no longer triggers when the condition contains a `return` [#14338](https://github.com/rust-lang/rust-clippy/pull/14338) +* [`undocumented_unsafe_blocks`] no longer triggers on trait/impl items [#13888](https://github.com/rust-lang/rust-clippy/pull/13888) +* [`manual_slice_fill`] no longer triggers due to missing index checks [#14193](https://github.com/rust-lang/rust-clippy/pull/14193) +* [`useless_asref`] no longer suggests using `.clone()` if the target type doesn't implement `Clone` [#14174](https://github.com/rust-lang/rust-clippy/pull/14174) +* [`unnecessary_safety_comment`] no longer triggers on desugared assign [#14371](https://github.com/rust-lang/rust-clippy/pull/14371) +* [`unnecessary_map_or`] no longer consumes the comparison value if it does not implement `Copy` [#14207](https://github.com/rust-lang/rust-clippy/pull/14207) +* [`let_and_return`] no longer triggers involving short-lived block temporary variables [#14180](https://github.com/rust-lang/rust-clippy/pull/14180) +* [`manual_async_fn`] no longer emits suggestions inside macros [#14142](https://github.com/rust-lang/rust-clippy/pull/14142) +* [`use_self`] skips analysis inside macro expansions of a `impl Self` block [#13128](https://github.com/rust-lang/rust-clippy/pull/13128) +* [`double_ended_iterator_last`] no longer triggers on non-reference immutable receiver [#14140](https://github.com/rust-lang/rust-clippy/pull/14140) + +### ICE Fixes + +* [`macro_use_imports`] Fix ICE when checking attributes + [#14317](https://github.com/rust-lang/rust-clippy/pull/14317) +* [`doc_nested_refdefs`] Fix ICE by avoiding invalid ranges + [#14308](https://github.com/rust-lang/rust-clippy/pull/14308) +* [`just_underscores_and_digits`] Fix ICE in error recovery scenario + [#14168](https://github.com/rust-lang/rust-clippy/pull/14168) +* [`declare_interior_mutable_const`], [`borrow_interior_mutable_const`] Fix ICE by properly resolving `::AssocT` projections + [#14125](https://github.com/rust-lang/rust-clippy/pull/14125) + +### Documentation Improvements + +* [`struct_excessive_bools`] Documentation improved with rationale + [#14351](https://github.com/rust-lang/rust-clippy/pull/14351) + +### Others + +* Use edition=2021 in `rustc_tools_util` + [#14211](https://github.com/rust-lang/rust-clippy/pull/14211) +* Fix rustc_tools_util's `version.host_compiler` release channel, expose the rustc version, and add tests + [#14123](https://github.com/rust-lang/rust-clippy/pull/14123) +* Make UI test annotations mandatory + [#11421](https://github.com/rust-lang/rust-clippy/pull/11421) + [#14388](https://github.com/rust-lang/rust-clippy/pull/14388) + [#14393](https://github.com/rust-lang/rust-clippy/pull/14393) ## Rust 1.86 @@ -5583,6 +5681,7 @@ Released 2018-09-13 [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr [`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied +[`cloned_ref_to_slice_refs`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_ref_to_slice_refs [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned @@ -5594,6 +5693,7 @@ Released 2018-09-13 [`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`confusing_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#confusing_method_to_numeric_cast [`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator @@ -6383,6 +6483,7 @@ Released 2018-09-13 [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero [`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests +[`allow-exact-repetitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-exact-repetitions [`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests @@ -6435,6 +6536,7 @@ Released 2018-09-13 [`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length [`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold +[`missing-docs-allow-unused`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-allow-unused [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items [`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 3b33f7190638..45ba2f078be7 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -77,7 +77,7 @@ debugging to find the actual problem behind the issue. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of -an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful. +an AST expression). [`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 1cfc1c196c0b..c7c06afb6129 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" @@ -28,7 +28,7 @@ clippy_lints = { path = "clippy_lints" } clippy_utils = { path = "clippy_utils" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } clippy_lints_internal = { path = "clippy_lints_internal", optional = true } -tempfile = { version = "3.3", optional = true } +tempfile = { version = "3.20", optional = true } termize = "0.1" color-print = "0.3.4" anstream = "0.6.18" @@ -47,7 +47,6 @@ pulldown-cmark = { version = "0.11", default-features = false, features = ["html askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } # UI test dependencies -clippy_utils = { path = "clippy_utils" } if_chain = "1.0" quote = "1.0.25" syn = { version = "2.0", features = ["full"] } diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index e5e82ede4fdf..2b89e94cf8f4 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -416,7 +416,7 @@ In our example, `is_foo_fn` looks like: fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { match fn_kind { - FnKind::Fn(_, ident, ..) => { + FnKind::Fn(_, _, Fn { ident, .. }) => { // check if `fn` name is `foo` ident.name.as_str() == "foo" } diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index 4219724ed5df..cdbbe76bdb08 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -145,42 +145,32 @@ unclear to you. If you are hacking on Clippy and want to install it from source, do the following: -First, take note of the toolchain -[override](https://rust-lang.github.io/rustup/overrides.html) in -`/rust-toolchain.toml`. We will use this override to install Clippy into the right -toolchain. - -> Tip: You can view the active toolchain for the current directory with `rustup -> show active-toolchain`. - From the Clippy project root, run the following command to build the Clippy -binaries and copy them into the toolchain directory. This will override the -currently installed Clippy component. +binaries and copy them into the toolchain directory. This will create a new +toolchain called `clippy` by default, see `cargo dev setup toolchain --help` +for other options. ```terminal -cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" +cargo dev setup toolcahin ``` -Now you may run `cargo clippy` in any project, using the toolchain where you -just installed Clippy. +Now you may run `cargo +clippy clippy` in any project using the new toolchain. ```terminal cd my-project -cargo +nightly-2021-07-01 clippy +cargo +clippy clippy ``` ...or `clippy-driver` ```terminal -clippy-driver +nightly-2021-07-01 +clippy-driver +clippy ``` -If you need to restore the default Clippy installation, run the following (from -the Clippy project root). +If you no longer need the toolchain it can be uninstalled using `rustup`: ```terminal -rustup component remove clippy -rustup component add clippy +rustup toolchain uninstall clippy ``` > **DO NOT** install using `cargo install --path . --force` since this will diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 2e39f279eae4..e23b32039c90 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -86,7 +86,7 @@ arguments have to be checked separately. ```rust use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{paths, match_def_path}; +use clippy_utils::paths; use rustc_span::symbol::sym; use rustc_hir::LangItem; @@ -108,7 +108,7 @@ impl LateLintPass<'_> for MyStructLint { // 3. Using the type path // This method should be avoided if possible - if match_def_path(cx, def_id, &paths::RESULT) { + if paths::RESULT.matches_ty(cx, ty) { // The type is a `core::result::Result` } } diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index 2b2c096b0496..eede6b78d921 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -51,7 +51,9 @@ Once you've got the correct commit range, run util/fetch_prs_between.sh commit1 commit2 > changes.txt ``` -and open that file in your editor of choice. +where `commit2` is the commit hash from the previous command and `commit1` +is the commit hash from the current CHANGELOG file. +Open `changes.txt` file in your editor of choice. When updating the changelog it's also a good idea to make sure that `commit1` is already correct in the current changelog. @@ -60,8 +62,8 @@ already correct in the current changelog. The above script should have dumped all the relevant PRs to the file you specified. It should have filtered out most of the irrelevant PRs already, but -it's a good idea to do a manual cleanup pass where you look for more irrelevant -PRs. If you're not sure about some PRs, just leave them in for the review and +it's a good idea to do a manual cleanup pass and choose valuable PRs. +If you're not sure about some PRs, just leave them in for the review and ask for feedback. With the PRs filtered, you can start to take each PR and move the `changelog: ` @@ -74,10 +76,9 @@ The order should roughly be: 2. Moves or deprecations of lints 3. Changes that expand what code existing lints cover 4. False positive fixes -5. Suggestion fixes/improvements -6. ICE fixes -7. Documentation improvements -8. Others +5. ICE fixes +6. Documentation improvements +7. Others As section headers, we use: @@ -91,7 +92,6 @@ As section headers, we use: ### Enhancements ### False Positive Fixes -### Suggestion Fixes/Improvements ### ICE Fixes ### Documentation Improvements ### Others @@ -112,7 +112,16 @@ that label in the changelog. If you can, remove the `beta-accepted` labels ### 4. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added -lints contain the correct version. +lints contain the correct version. +In order to find lints that need a version update, go through the lints in the +"New Lints" section and run the following command for each lint name: + +``` +grep -rB1 "pub $LINT_NAME" . +``` + +The version shown should match the version of the release the changelog is +written for. If not, update the version to the changelog version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ diff --git a/src/tools/clippy/book/src/development/trait_checking.md b/src/tools/clippy/book/src/development/trait_checking.md index b7d229ccc193..cc4eb966f596 100644 --- a/src/tools/clippy/book/src/development/trait_checking.md +++ b/src/tools/clippy/book/src/development/trait_checking.md @@ -73,22 +73,24 @@ impl LateLintPass<'_> for CheckDropTraitLint { ## Using Type Path If neither diagnostic item nor a language item is available, we can use -[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait -implementation. +[`clippy_utils::paths`][paths] to determine get a trait's `DefId`. > **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item. -Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` : +Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html): ```rust -use clippy_utils::{match_trait_method, paths}; +use clippy_utils::{implements_trait, paths}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { +impl LateLintPass<'_> for CheckIterStep { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) { - println!("`expr` implements `CORE_ITER_CLONED` trait!"); + let ty = cx.typeck_results().expr_ty(expr); + if let Some(trait_def_id) = paths::ITER_STEP.first(cx) + && implements_trait(cx, ty, trait_def_id, &[]) + { + println!("`expr` implements the `core::iter::Step` trait!"); } } } diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 2314d1beac7e..0db4182dbcdb 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` * [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) +## `allow-exact-repetitions` +Whether an item should be allowed to have the same name as its containing module + +**Default Value:** `true` + +--- +**Affected lints:** +* [`module_name_repetitions`](https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions) + + ## `allow-expect-in-consts` Whether `expect` should be allowed in code always evaluated at compile time @@ -734,6 +744,16 @@ Minimum chars an ident can have, anything below or equal to this will be linted. * [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) +## `missing-docs-allow-unused` +Whether to allow fields starting with an underscore to skip documentation requirements + +**Default Value:** `false` + +--- +**Affected lints:** +* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) + + ## `missing-docs-in-crate-items` Whether to **only** check for missing documentation in items visible within the current crate. For example, `pub(crate)` items. @@ -839,6 +859,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index 0a7724bbe4e6..77573105d86a 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -7,14 +7,11 @@ lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" -allow-invalid = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" -allow-invalid = true [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" -allow-invalid = true diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 93fd2e35d1ba..1134b0e97af2 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version edition = "2024" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 511cb84527d8..ad0aea39d41a 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -360,6 +360,9 @@ define_Conf! { /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` #[lints(dbg_macro)] allow_dbg_in_tests: bool = false, + /// Whether an item should be allowed to have the same name as its containing module + #[lints(module_name_repetitions)] + allow_exact_repetitions: bool = true, /// Whether `expect` should be allowed in code always evaluated at compile time #[lints(expect_used)] allow_expect_in_consts: bool = true, @@ -675,6 +678,9 @@ define_Conf! { /// Minimum chars an ident can have, anything below or equal to this will be linted. #[lints(min_ident_chars)] min_ident_chars_threshold: u64 = 1, + /// Whether to allow fields starting with an underscore to skip documentation requirements + #[lints(missing_docs_in_private_items)] + missing_docs_allow_unused: bool = false, /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. #[lints(missing_docs_in_private_items)] @@ -756,6 +762,7 @@ define_Conf! { same_item_push, seek_from_current, seek_rewind, + to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index c227b8900b74..67904b4fcdc8 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, array_windows, let_chains)] +#![feature(rustc_private)] #![warn( trivial_casts, trivial_numeric_casts, @@ -12,6 +12,7 @@ rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] +#![deny(clippy::derive_deserialize_allowing_unknown)] extern crate rustc_data_structures; extern crate rustc_errors; diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 5949eaca7bc1..f64eefa0c232 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -1,16 +1,18 @@ +use clippy_utils::paths::{PathNS, find_crates, lookup_path}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; use rustc_hir::PrimTy; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use serde::de::{self, Deserializer, Visitor}; use serde::{Deserialize, Serialize, ser}; use std::collections::HashMap; use std::fmt; #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Rename { pub path: String, pub rename: String, @@ -58,7 +60,7 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath( tcx: TyCtxt<'_>, disallowed_paths: &'static [DisallowedPath], + ns: PathNS, def_kind_predicate: impl Fn(DefKind) -> bool, predicate_description: &str, allow_prim_tys: bool, @@ -145,57 +148,47 @@ pub fn create_disallowed_map( FxHashMap::default(); for disallowed_path in disallowed_paths { let path = disallowed_path.path(); - let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::>()); + let sym_path: Vec = path.split("::").map(Symbol::intern).collect(); + let mut resolutions = lookup_path(tcx, ns, &sym_path); + resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id))); - let mut found_def_id = None; - let mut found_prim_ty = false; - resolutions.retain(|res| match res { - Res::Def(def_kind, def_id) => { - found_def_id = Some(*def_id); - def_kind_predicate(*def_kind) - }, - Res::PrimTy(_) => { - found_prim_ty = true; - allow_prim_tys - }, - _ => false, - }); + let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice() + && let Some(prim) = PrimTy::from_name(name) + { + (allow_prim_tys.then_some(prim), true) + } else { + (None, false) + }; - if resolutions.is_empty() { - let span = disallowed_path.span(); - - if let Some(def_id) = found_def_id { - tcx.sess.dcx().span_warn( - span, - format!( - "expected a {predicate_description}, found {} {}", - tcx.def_descr_article(def_id), - tcx.def_descr(def_id) - ), - ); + if resolutions.is_empty() + && prim_ty.is_none() + && !disallowed_path.allow_invalid + // Don't warn about unloaded crates: + // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 + && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty()) + { + // Relookup the path in an arbitrary namespace to get a good `expected, found` message + let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path); + let message = if let Some(&def_id) = found_def_ids.first() { + let (article, description) = tcx.article_and_description(def_id); + format!("expected a {predicate_description}, found {article} {description}") } else if found_prim_ty { - tcx.sess.dcx().span_warn( - span, - format!("expected a {predicate_description}, found a primitive type",), - ); - } else if !disallowed_path.allow_invalid { - tcx.sess.dcx().span_warn( - span, - format!("`{path}` does not refer to an existing {predicate_description}"), - ); - } + format!("expected a {predicate_description}, found a primitive type") + } else { + format!("`{path}` does not refer to a reachable {predicate_description}") + }; + tcx.sess + .dcx() + .struct_span_warn(disallowed_path.span(), message) + .with_help("add `allow-invalid = true` to the entry to suppress this warning") + .emit(); } - for res in resolutions { - match res { - Res::Def(_, def_id) => { - def_ids.insert(def_id, (path, disallowed_path)); - }, - Res::PrimTy(ty) => { - prim_tys.insert(ty, (path, disallowed_path)); - }, - _ => unreachable!(), - } + for def_id in resolutions { + def_ids.insert(def_id, (path, disallowed_path)); + } + if let Some(ty) = prim_ty { + prim_tys.insert(ty, (path, disallowed_path)); } } diff --git a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs new file mode 100644 index 000000000000..bf0e77710469 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs @@ -0,0 +1,174 @@ +use crate::update_lints::{ + DeprecatedLint, DeprecatedLints, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints, +}; +use crate::utils::{UpdateMode, Version}; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +/// Runs the `deprecate` command +/// +/// This does the following: +/// * Adds an entry to `deprecated_lints.rs`. +/// * Removes the lint declaration (and the entire file if applicable) +/// +/// # Panics +/// +/// If a file path could not read from or written to +pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { + let prefixed_name = if name.starts_with("clippy::") { + name.to_owned() + } else { + format!("clippy::{name}") + }; + let stripped_name = &prefixed_name[8..]; + + let mut lints = find_lint_decls(); + let DeprecatedLints { + renamed: renamed_lints, + deprecated: mut deprecated_lints, + file: mut deprecated_file, + contents: mut deprecated_contents, + deprecated_end, + .. + } = read_deprecated_lints(); + + let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { + eprintln!("error: failed to find lint `{name}`"); + return; + }; + + let mod_path = { + let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); + if mod_path.is_dir() { + mod_path = mod_path.join("mod"); + } + + mod_path.set_extension("rs"); + mod_path + }; + + if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { + deprecated_contents.insert_str( + deprecated_end as usize, + &format!( + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + clippy_version.rust_display(), + prefixed_name, + reason, + ), + ); + deprecated_file.replace_contents(deprecated_contents.as_bytes()); + drop(deprecated_file); + + deprecated_lints.push(DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + }); + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } +} + +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { + fn remove_lint(name: &str, lints: &mut Vec) { + lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); + } + + fn remove_test_assets(name: &str) { + let test_file_stem = format!("tests/ui/{name}"); + let path = Path::new(&test_file_stem); + + // Some lints have their own directories, delete them + if path.is_dir() { + let _ = fs::remove_dir_all(path); + return; + } + + // Remove all related test files + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); + } + + fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { + let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { + content + .find("declare_lint_pass!") + .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) + }); + let mut impl_lint_pass_end = content[impl_lint_pass_start..] + .find(']') + .expect("failed to find `impl_lint_pass` terminator"); + + impl_lint_pass_end += impl_lint_pass_start; + if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { + let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); + for c in content[lint_name_end..impl_lint_pass_end].chars() { + // Remove trailing whitespace + if c == ',' || c.is_whitespace() { + lint_name_end += 1; + } else { + break; + } + } + + content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); + } + } + + if path.exists() + && let Some(lint) = lints.iter().find(|l| l.name == name) + { + if lint.module == name { + // The lint name is the same as the file, we can just delete the entire file + fs::remove_file(path)?; + } else { + // We can't delete the entire file, just remove the declaration + + if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { + // Remove clippy_lints/src/some_mod/some_lint.rs + let mut lint_mod_path = path.to_path_buf(); + lint_mod_path.set_file_name(name); + lint_mod_path.set_extension("rs"); + + let _ = fs::remove_file(lint_mod_path); + } + + let mut content = + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + + eprintln!( + "warn: you will have to manually remove any code related to `{name}` from `{}`", + path.display() + ); + + assert!( + content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + "error: `{}` does not contain lint `{}`'s declaration", + path.display(), + lint.name + ); + + // Remove lint declaration (declare_clippy_lint!) + content.replace_range(lint.declaration_range.clone(), ""); + + // Remove the module declaration (mod xyz;) + let mod_decl = format!("\nmod {name};"); + content = content.replacen(&mod_decl, "", 1); + + remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); + fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); + } + + remove_test_assets(name); + remove_lint(name, lints); + return Ok(true); + } + + Ok(false) +} diff --git a/src/tools/clippy/clippy_dev/src/dogfood.rs b/src/tools/clippy/clippy_dev/src/dogfood.rs index 05fa24d8d4ee..7e9d92458d05 100644 --- a/src/tools/clippy/clippy_dev/src/dogfood.rs +++ b/src/tools/clippy/clippy_dev/src/dogfood.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, exit_if_err}; +use crate::utils::exit_if_err; use std::process::Command; /// # Panics @@ -8,8 +8,7 @@ use std::process::Command; pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { let mut cmd = Command::new("cargo"); - cmd.current_dir(clippy_project_root()) - .args(["test", "--test", "dogfood"]) + cmd.args(["test", "--test", "dogfood"]) .args(["--features", "internal"]) .args(["--", "dogfood_clippy", "--nocapture"]); diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index bdddf46a2cb1..b4c13213f552 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,4 +1,3 @@ -use crate::utils::clippy_project_root; use itertools::Itertools; use rustc_lexer::{TokenKind, tokenize}; use shell_escape::escape; @@ -104,15 +103,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { Field, } - let path: PathBuf = [ - clippy_project_root().as_path(), - "clippy_config".as_ref(), - "src".as_ref(), - "conf.rs".as_ref(), - ] - .into_iter() - .collect(); - let text = fs::read_to_string(&path)?; + let path = "clippy_config/src/conf.rs"; + let text = fs::read_to_string(path)?; let (pre, conf) = text .split_once("define_Conf! {\n") @@ -203,7 +195,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {}, _ => { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + i), format!("unexpected token `{}`", &conf[i..i + t.len as usize]), )); @@ -213,7 +205,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if !matches!(state, State::Field) { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + conf.len()), "incomplete field".into(), )); @@ -260,18 +252,16 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if check { return Err(Error::CheckFailed); } - fs::write(&path, new_text.as_bytes())?; + fs::write(path, new_text.as_bytes())?; } Ok(()) } fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { - let project_root = clippy_project_root(); - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to // format because rustfmt would also format the entire rustc repo as it is a local // dependency - if fs::read_to_string(project_root.join("Cargo.toml")) + if fs::read_to_string("Cargo.toml") .expect("Failed to read clippy Cargo.toml") .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { @@ -280,12 +270,12 @@ fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { check_for_rustfmt(context)?; - cargo_fmt(context, project_root.as_path())?; - cargo_fmt(context, &project_root.join("clippy_dev"))?; - cargo_fmt(context, &project_root.join("rustc_tools_util"))?; - cargo_fmt(context, &project_root.join("lintcheck"))?; + cargo_fmt(context, ".".as_ref())?; + cargo_fmt(context, "clippy_dev".as_ref())?; + cargo_fmt(context, "rustc_tools_util".as_ref())?; + cargo_fmt(context, "lintcheck".as_ref())?; - let chunks = WalkDir::new(project_root.join("tests")) + let chunks = WalkDir::new("tests") .into_iter() .filter_map(|entry| { let entry = entry.expect("failed to find tests"); diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index c1ffaf269c6f..e237a05b2530 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,5 +1,4 @@ -#![feature(let_chains)] -#![feature(rustc_private)] +#![feature(rustc_private, if_let_guard, let_chains)] #![warn( trivial_casts, trivial_numeric_casts, @@ -15,11 +14,13 @@ extern crate rustc_driver; extern crate rustc_lexer; extern crate rustc_literal_escaper; +pub mod deprecate_lint; pub mod dogfood; pub mod fmt; pub mod lint; pub mod new_lint; pub mod release; +pub mod rename_lint; pub mod serve; pub mod setup; pub mod sync; diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 83f8e66b3347..5dce0be742b2 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -3,11 +3,18 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use clap::{Args, Parser, Subcommand}; -use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils}; +use clippy_dev::{ + deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, utils, +}; use std::convert::Infallible; +use std::env; fn main() { let dev = Dev::parse(); + let clippy = utils::ClippyInfo::search_for_manifest(); + if let Err(e) = env::set_current_dir(&clippy.path) { + panic!("error setting current directory to `{}`: {e}", clippy.path.display()); + } match dev.command { DevCommand::Bless => { @@ -20,22 +27,14 @@ fn main() { allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), - DevCommand::UpdateLints { print_only, check } => { - if print_only { - update_lints::print_lints(); - } else if check { - update_lints::update(utils::UpdateMode::Check); - } else { - update_lints::update(utils::UpdateMode::Change); - } - }, + DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), DevCommand::NewLint { pass, name, category, r#type, msrv, - } => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) { + } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { Ok(()) => update_lints::update(utils::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), }, @@ -79,13 +78,18 @@ fn main() { old_name, new_name, uplift, - } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason), + } => rename_lint::rename( + clippy.version, + &old_name, + new_name.as_ref().unwrap_or(&old_name), + uplift, + ), + DevCommand::Deprecate { name, reason } => deprecate_lint::deprecate(clippy.version, &name, &reason), DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), }, DevCommand::Release(ReleaseCommand { subcommand }) => match subcommand { - ReleaseSubcommand::BumpVersion => release::bump_version(), + ReleaseSubcommand::BumpVersion => release::bump_version(clippy.version), }, } } @@ -135,11 +139,6 @@ enum DevCommand { /// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n} /// * all lints are registered in the lint store UpdateLints { - #[arg(long)] - /// Print a table of lints to STDOUT - /// - /// This does not include deprecated and internal lints. (Does not modify any files) - print_only: bool, #[arg(long)] /// Checks that `cargo dev update_lints` has been run. Used on CI. check: bool, diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 96e12706c9e2..4121daa85e6d 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, clippy_version}; +use crate::utils::{RustSearcher, Token, Version}; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; use std::fmt::{self, Write as _}; @@ -22,11 +22,11 @@ impl fmt::Display for Pass { } struct LintData<'a> { + clippy_version: Version, pass: Pass, name: &'a str, category: &'a str, ty: Option<&'a str>, - project_root: PathBuf, } trait Context { @@ -50,18 +50,25 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create(pass: Pass, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { +pub fn create( + clippy_version: Version, + pass: Pass, + name: &str, + category: &str, + mut ty: Option<&str>, + msrv: bool, +) -> io::Result<()> { if category == "cargo" && ty.is_none() { // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` ty = Some("cargo"); } let lint = LintData { + clippy_version, pass, name, category, ty, - project_root: clippy_project_root(), }; create_lint(&lint, msrv).context("Unable to create lint implementation")?; @@ -88,7 +95,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { } else { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); - write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; + write_file(&lint_path, lint_contents.as_bytes())?; println!("Generated lint file: `{lint_path}`"); Ok(()) @@ -115,8 +122,7 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { } if lint.category == "cargo" { - let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); - let test_dir = lint.project_root.join(&relative_test_dir); + let test_dir = format!("tests/ui-cargo/{}", lint.name); fs::create_dir(&test_dir)?; create_project_layout( @@ -134,11 +140,11 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { false, )?; - println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); + println!("Generated test directories: `{test_dir}/pass`, `{test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); let test_contents = get_test_file_contents(lint.name, msrv); - write_file(lint.project_root.join(&test_path), test_contents)?; + write_file(&test_path, test_contents)?; println!("Generated test file: `{test_path}`"); } @@ -193,11 +199,6 @@ fn to_camel_case(name: &str) -> String { .collect() } -pub(crate) fn get_stabilization_version() -> String { - let (minor, patch) = clippy_version(); - format!("{minor}.{patch}.0") -} - fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { let mut test = formatdoc!( r" @@ -292,7 +293,11 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ); } - let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); + let _: fmt::Result = writeln!( + result, + "{}", + get_lint_declaration(lint.clippy_version, &name_upper, category) + ); if enable_msrv { let _: fmt::Result = writedoc!( @@ -330,7 +335,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result } -fn get_lint_declaration(name_upper: &str, category: &str) -> String { +fn get_lint_declaration(version: Version, name_upper: &str, category: &str) -> String { let justification_heading = if category == "restriction" { "Why restrict this?" } else { @@ -355,9 +360,8 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String { pub {name_upper}, {category}, "default lint description" - }} - "#, - get_stabilization_version(), + }}"#, + version.rust_display(), ) } @@ -371,7 +375,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R _ => {}, } - let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}")); + let ty_dir = PathBuf::from(format!("clippy_lints/src/{ty}")); assert!( ty_dir.exists() && ty_dir.is_dir(), "Directory `{}` does not exist!", @@ -441,95 +445,25 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R #[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { - use super::update_lints::{LintDeclSearchResult, match_tokens}; - use rustc_lexer::TokenKind; - let lint_name_upper = lint.name.to_uppercase(); let mut file_contents = fs::read_to_string(path)?; assert!( - !file_contents.contains(&lint_name_upper), + !file_contents.contains(&format!("pub {lint_name_upper},")), "Lint `{}` already defined in `{}`", lint.name, path.display() ); - let mut offset = 0usize; - let mut last_decl_curly_offset = None; - let mut lint_context = None; - - let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &file_contents[range.clone()], - range, - } - }); - - // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl - while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) { - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - - match content { - "declare_clippy_lint" => { - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - if let Some(LintDeclSearchResult { range, .. }) = - iter.find(|result| result.token_kind == TokenKind::CloseBrace) - { - last_decl_curly_offset = Some(range.end); - } - }, - "impl" => { - let mut token = iter.next(); - match token { - // matches <'foo> - Some(LintDeclSearchResult { - token_kind: TokenKind::Lt, - .. - }) => { - match_tokens!(iter, Lifetime { .. } Gt); - token = iter.next(); - }, - None => break, - _ => {}, - } - - if let Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - content, - .. - }) = token - { - // Get the appropriate lint context struct - lint_context = match content { - "LateLintPass" => Some("LateContext"), - "EarlyLintPass" => Some("EarlyContext"), - _ => continue, - }; - } - }, - _ => {}, - } - } - - drop(iter); - - let last_decl_curly_offset = - last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())); - let lint_context = - lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())); + let (lint_context, lint_decl_end) = parse_mod_file(path, &file_contents); // Add the lint declaration to `mod.rs` - file_contents.replace_range( - // Remove the trailing newline, which should always be present - last_decl_curly_offset..=last_decl_curly_offset, - &format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)), + file_contents.insert_str( + lint_decl_end, + &format!( + "\n\n{}", + get_lint_declaration(lint.clippy_version, &lint_name_upper, lint.category) + ), ); // Add the lint to `impl_lint_pass`/`declare_lint_pass` @@ -580,6 +514,41 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> Ok(lint_context) } +// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl +fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { + #[allow(clippy::enum_glob_use)] + use Token::*; + + let mut context = None; + let mut decl_end = None; + let mut searcher = RustSearcher::new(contents); + while let Some(name) = searcher.find_capture_token(CaptureIdent) { + match name { + "declare_clippy_lint" => { + if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) { + decl_end = Some(searcher.pos()); + } + }, + "impl" => { + let mut capture = ""; + if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { + match capture { + "LateLintPass" => context = Some("LateContext"), + "EarlyLintPass" => context = Some("EarlyContext"), + _ => {}, + } + } + }, + _ => {}, + } + } + + ( + context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())), + decl_end.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())) as usize, + ) +} + #[test] fn test_camel_case() { let s = "a_lint"; diff --git a/src/tools/clippy/clippy_dev/src/release.rs b/src/tools/clippy/clippy_dev/src/release.rs index ac7551687010..d3b1a7ff3201 100644 --- a/src/tools/clippy/clippy_dev/src/release.rs +++ b/src/tools/clippy/clippy_dev/src/release.rs @@ -1,27 +1,27 @@ +use crate::utils::{FileUpdater, Version, update_text_region_fn}; use std::fmt::Write; -use std::path::Path; -use crate::utils::{UpdateMode, clippy_version, replace_region_in_file}; - -const CARGO_TOML_FILES: [&str; 4] = [ +static CARGO_TOML_FILES: &[&str] = &[ "clippy_config/Cargo.toml", "clippy_lints/Cargo.toml", "clippy_utils/Cargo.toml", "Cargo.toml", ]; -pub fn bump_version() { - let (minor, mut patch) = clippy_version(); - patch += 1; - for file in &CARGO_TOML_FILES { - replace_region_in_file( - UpdateMode::Change, - Path::new(file), - "# begin autogenerated version\n", - "# end autogenerated version", - |res| { - writeln!(res, "version = \"0.{minor}.{patch}\"").unwrap(); - }, +pub fn bump_version(mut version: Version) { + version.minor += 1; + + let mut updater = FileUpdater::default(); + for file in CARGO_TOML_FILES { + updater.update_file( + file, + &mut update_text_region_fn( + "# begin autogenerated version\n", + "# end autogenerated version", + |dst| { + writeln!(dst, "version = \"{}\"", version.toml_display()).unwrap(); + }, + ), ); } } diff --git a/src/tools/clippy/clippy_dev/src/rename_lint.rs b/src/tools/clippy/clippy_dev/src/rename_lint.rs new file mode 100644 index 000000000000..9e7e5d97f021 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/rename_lint.rs @@ -0,0 +1,194 @@ +use crate::update_lints::{ + DeprecatedLints, RenamedLint, find_lint_decls, gen_renamed_lints_test_fn, generate_lint_files, + read_deprecated_lints, +}; +use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, try_rename_file}; +use std::ffi::OsStr; +use std::path::Path; +use walkdir::WalkDir; + +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[allow(clippy::too_many_lines)] +pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{old_name}` should not contain the `{prefix}` prefix"); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{new_name}` should not contain the `{prefix}` prefix"); + } + + let mut updater = FileUpdater::default(); + let mut lints = find_lint_decls(); + let DeprecatedLints { + renamed: mut renamed_lints, + deprecated: deprecated_lints, + file: mut deprecated_file, + contents: mut deprecated_contents, + renamed_end, + .. + } = read_deprecated_lints(); + + let mut old_lint_index = None; + let mut found_new_name = false; + for (i, lint) in lints.iter().enumerate() { + if lint.name == old_name { + old_lint_index = Some(i); + } else if lint.name == new_name { + found_new_name = true; + } + } + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); + + let lint = RenamedLint { + old_name: format!("clippy::{old_name}"), + new_name: if uplift { + new_name.into() + } else { + format!("clippy::{new_name}") + }, + }; + + // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in + // case. + assert!( + !renamed_lints.iter().any(|l| lint.old_name == l.old_name), + "`{old_name}` has already been renamed" + ); + assert!( + !deprecated_lints.iter().any(|l| lint.old_name == l.name), + "`{old_name}` has already been deprecated" + ); + + // Update all lint level attributes. (`clippy::lint_name`) + let replacements = &[(&*lint.old_name, &*lint.new_name)]; + let replacer = StringReplacer::new(replacements); + for file in WalkDir::new(".").into_iter().map(Result::unwrap).filter(|f| { + let name = f.path().file_name(); + let ext = f.path().extension(); + (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) + && name != Some(OsStr::new("rename.rs")) + && name != Some(OsStr::new("deprecated_lints.rs")) + }) { + updater.update_file(file.path(), &mut replacer.replace_ident_fn()); + } + + deprecated_contents.insert_str( + renamed_end as usize, + &format!( + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + clippy_version.rust_display(), + lint.old_name, + lint.new_name, + ), + ); + deprecated_file.replace_contents(deprecated_contents.as_bytes()); + drop(deprecated_file); + + renamed_lints.push(lint); + renamed_lints.sort_by(|lhs, rhs| { + lhs.new_name + .starts_with("clippy::") + .cmp(&rhs.new_name.starts_with("clippy::")) + .reverse() + .then_with(|| lhs.old_name.cmp(&rhs.old_name)) + }); + + if uplift { + updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); + println!( + "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." + ); + } else if found_new_name { + updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); + println!( + "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." + ); + } else { + // Rename the lint struct and source files sharing a name with the lint. + let lint = &mut lints[old_lint_index]; + let old_name_upper = old_name.to_uppercase(); + let new_name_upper = new_name.to_uppercase(); + lint.name = new_name.into(); + + // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. + if try_rename_file( + Path::new(&format!("tests/ui/{old_name}.rs")), + Path::new(&format!("tests/ui/{new_name}.rs")), + ) { + try_rename_file( + Path::new(&format!("tests/ui/{old_name}.stderr")), + Path::new(&format!("tests/ui/{new_name}.stderr")), + ); + try_rename_file( + Path::new(&format!("tests/ui/{old_name}.fixed")), + Path::new(&format!("tests/ui/{new_name}.fixed")), + ); + } + + // Try to rename the file containing the lint if the file name matches the lint's name. + let replacements; + let replacements = if lint.module == old_name + && try_rename_file( + Path::new(&format!("clippy_lints/src/{old_name}.rs")), + Path::new(&format!("clippy_lints/src/{new_name}.rs")), + ) { + // Edit the module name in the lint list. Note there could be multiple lints. + for lint in lints.iter_mut().filter(|l| l.module == old_name) { + lint.module = new_name.into(); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else if !lint.module.contains("::") + // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), + Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), + ) + { + // Edit the module name in the lint list. Note there could be multiple lints, or none. + let renamed_mod = format!("{}::{old_name}", lint.module); + for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { + lint.module = format!("{}::{new_name}", lint.module); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else { + replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; + &replacements[0..1] + }; + + // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being + // renamed. + let replacer = StringReplacer::new(replacements); + for file in WalkDir::new("clippy_lints/src") { + let file = file.expect("error reading `clippy_lints/src`"); + if file + .path() + .as_os_str() + .to_str() + .is_some_and(|x| x.ends_with("*.rs") && x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") + { + updater.update_file(file.path(), &mut replacer.replace_ident_fn()); + } + } + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("{old_name} has been successfully renamed"); + } + + println!("note: `cargo uitest` still needs to be run to update the test results"); +} diff --git a/src/tools/clippy/clippy_dev/src/sync.rs b/src/tools/clippy/clippy_dev/src/sync.rs index a6b65e561c22..c699b0d7b959 100644 --- a/src/tools/clippy/clippy_dev/src/sync.rs +++ b/src/tools/clippy/clippy_dev/src/sync.rs @@ -1,33 +1,18 @@ -use std::fmt::Write; -use std::path::Path; - +use crate::utils::{FileUpdater, update_text_region_fn}; use chrono::offset::Utc; - -use crate::utils::{UpdateMode, replace_region_in_file}; +use std::fmt::Write; pub fn update_nightly() { - // Update rust-toolchain nightly version let date = Utc::now().format("%Y-%m-%d").to_string(); - replace_region_in_file( - UpdateMode::Change, - Path::new("rust-toolchain.toml"), + let update = &mut update_text_region_fn( "# begin autogenerated nightly\n", "# end autogenerated nightly", - |res| { - writeln!(res, "channel = \"nightly-{date}\"").unwrap(); + |dst| { + writeln!(dst, "channel = \"nightly-{date}\"").unwrap(); }, ); - // Update clippy_utils nightly version - replace_region_in_file( - UpdateMode::Change, - Path::new("clippy_utils/README.md"), - "\n", - "", - |res| { - writeln!(res, "```").unwrap(); - writeln!(res, "nightly-{date}").unwrap(); - writeln!(res, "```").unwrap(); - }, - ); + let mut updater = FileUpdater::default(); + updater.update_file("rust-toolchain.toml", update); + updater.update_file("clippy_utils/README.md", update); } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index d848a97f86d2..0c861b729356 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,15 +1,12 @@ -use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file}; -use aho_corasick::AhoCorasickBuilder; +use crate::utils::{ + File, FileAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_file, update_text_region_fn, +}; use itertools::Itertools; -use rustc_lexer::{LiteralKind, TokenKind, tokenize}; -use rustc_literal_escaper::{Mode, unescape_unicode}; -use std::collections::{HashMap, HashSet}; -use std::ffi::OsStr; -use std::fmt::{self, Write}; -use std::fs::{self, OpenOptions}; -use std::io::{self, Read, Seek, Write as _}; +use std::collections::HashSet; +use std::fmt::Write; +use std::fs::OpenOptions; use std::ops::Range; -use std::path::{Path, PathBuf}; +use std::path::Path; use walkdir::{DirEntry, WalkDir}; const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ @@ -28,839 +25,322 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// /// Panics if a file path could not read from or then written to pub fn update(update_mode: UpdateMode) { - let (lints, deprecated_lints, renamed_lints) = gather_all(); - generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); + let lints = find_lint_decls(); + let DeprecatedLints { + renamed, deprecated, .. + } = read_deprecated_lints(); + generate_lint_files(update_mode, &lints, &deprecated, &renamed); } -fn generate_lint_files( +pub fn generate_lint_files( update_mode: UpdateMode, lints: &[Lint], - deprecated_lints: &[DeprecatedLint], - renamed_lints: &[RenamedLint], + deprecated: &[DeprecatedLint], + renamed: &[RenamedLint], ) { - let mut lints = lints.to_owned(); - lints.sort_by_key(|lint| lint.name.clone()); - - replace_region_in_file( + FileUpdater::default().update_files_checked( + "cargo dev update_lints", update_mode, - Path::new("README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(lints.len())).unwrap(); - }, - ); - - replace_region_in_file( - update_mode, - Path::new("book/src/README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(lints.len())).unwrap(); - }, - ); - - replace_region_in_file( - update_mode, - Path::new("CHANGELOG.md"), - "\n", - "", - |res| { - for lint in lints - .iter() - .map(|l| &*l.name) - .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ); - - // This has to be in lib.rs, otherwise rustfmt doesn't work - replace_region_in_file( - update_mode, - Path::new("clippy_lints/src/lib.rs"), - "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", - "// end lints modules, do not remove this comment, it’s used in `update_lints`", - |res| { - for lint_mod in lints.iter().map(|l| &l.module).unique().sorted() { - writeln!(res, "mod {lint_mod};").unwrap(); - } - }, - ); - - process_file( - "clippy_lints/src/declared_lints.rs", - update_mode, - &gen_declared_lints(lints.iter()), - ); - - let content = gen_deprecated_lints_test(deprecated_lints); - process_file("tests/ui/deprecated.rs", update_mode, &content); - - let content = gen_renamed_lints_test(renamed_lints); - process_file("tests/ui/rename.rs", update_mode, &content); -} - -pub fn print_lints() { - let (lints, _, _) = gather_all(); - let lint_count = lints.len(); - let grouped_by_lint_group = Lint::by_lint_group(lints.into_iter()); - - for (lint_group, mut lints) in grouped_by_lint_group { - println!("\n## {lint_group}"); - - lints.sort_by_key(|l| l.name.clone()); - - for lint in lints { - println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc); - } - } - - println!("there are {lint_count} lints"); -} - -/// Runs the `rename_lint` command. -/// -/// This does the following: -/// * Adds an entry to `renamed_lints.rs`. -/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). -/// * Renames the lint struct to the new name. -/// * Renames the module containing the lint struct to the new name if it shares a name with the -/// lint. -/// -/// # Panics -/// Panics for the following conditions: -/// * If a file path could not read from or then written to -/// * If either lint name has a prefix -/// * If `old_name` doesn't name an existing lint. -/// * If `old_name` names a deprecated or renamed lint. -#[allow(clippy::too_many_lines)] -pub fn rename(old_name: &str, new_name: &str, uplift: bool) { - if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{old_name}` should not contain the `{prefix}` prefix"); - } - if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{new_name}` should not contain the `{prefix}` prefix"); - } - - let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); - let mut old_lint_index = None; - let mut found_new_name = false; - for (i, lint) in lints.iter().enumerate() { - if lint.name == old_name { - old_lint_index = Some(i); - } else if lint.name == new_name { - found_new_name = true; - } - } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); - - let lint = RenamedLint { - old_name: format!("clippy::{old_name}"), - new_name: if uplift { - new_name.into() - } else { - format!("clippy::{new_name}") - }, - }; - - // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in - // case. - assert!( - !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{old_name}` has already been renamed" - ); - assert!( - !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{old_name}` has already been deprecated" - ); - - // Update all lint level attributes. (`clippy::lint_name`) - for file in WalkDir::new(clippy_project_root()) - .into_iter() - .map(Result::unwrap) - .filter(|f| { - let name = f.path().file_name(); - let ext = f.path().extension(); - (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) - && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("deprecated_lints.rs")) - }) - { - rewrite_file(file.path(), |s| { - replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) - }); - } - - let version = crate::new_lint::get_stabilization_version(); - rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { - insert_at_marker( - s, - "// end renamed lints. used by `cargo dev rename_lint`", - &format!( - "#[clippy::version = \"{version}\"]\n \ - (\"{}\", \"{}\"),\n ", - lint.old_name, lint.new_name, + &mut [ + ( + "README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), ), - ) - }); - - renamed_lints.push(lint); - renamed_lints.sort_by(|lhs, rhs| { - lhs.new_name - .starts_with("clippy::") - .cmp(&rhs.new_name.starts_with("clippy::")) - .reverse() - .then_with(|| lhs.old_name.cmp(&rhs.old_name)) - }); - - if uplift { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." - ); - } else if found_new_name { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." - ); - } else { - // Rename the lint struct and source files sharing a name with the lint. - let lint = &mut lints[old_lint_index]; - let old_name_upper = old_name.to_uppercase(); - let new_name_upper = new_name.to_uppercase(); - lint.name = new_name.into(); - - // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. - if try_rename_file( - Path::new(&format!("tests/ui/{old_name}.rs")), - Path::new(&format!("tests/ui/{new_name}.rs")), - ) { - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.stderr")), - Path::new(&format!("tests/ui/{new_name}.stderr")), - ); - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.fixed")), - Path::new(&format!("tests/ui/{new_name}.fixed")), - ); - } - - // Try to rename the file containing the lint if the file name matches the lint's name. - let replacements; - let replacements = if lint.module == old_name - && try_rename_file( - Path::new(&format!("clippy_lints/src/{old_name}.rs")), - Path::new(&format!("clippy_lints/src/{new_name}.rs")), - ) { - // Edit the module name in the lint list. Note there could be multiple lints. - for lint in lints.iter_mut().filter(|l| l.module == old_name) { - lint.module = new_name.into(); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else if !lint.module.contains("::") - // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` - && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), - Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), - ) - { - // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{old_name}", lint.module); - for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{new_name}", lint.module); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else { - replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; - &replacements[0..1] - }; - - // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being - // renamed. - for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs")) - { - rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); - } - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{old_name} has been successfully renamed"); - } - - println!("note: `cargo uitest` still needs to be run to update the test results"); -} - -/// Runs the `deprecate` command -/// -/// This does the following: -/// * Adds an entry to `deprecated_lints.rs`. -/// * Removes the lint declaration (and the entire file if applicable) -/// -/// # Panics -/// -/// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: &str) { - let prefixed_name = if name.starts_with("clippy::") { - name.to_owned() - } else { - format!("clippy::{name}") - }; - let stripped_name = &prefixed_name[8..]; - - let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { - eprintln!("error: failed to find lint `{name}`"); - return; - }; - - let mod_path = { - let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); - if mod_path.is_dir() { - mod_path = mod_path.join("mod"); - } - - mod_path.set_extension("rs"); - mod_path - }; - - let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs"); - - if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - let version = crate::new_lint::get_stabilization_version(); - rewrite_file(deprecated_lints_path, |s| { - insert_at_marker( - s, - "// end deprecated lints. used by `cargo dev deprecate_lint`", - &format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",), - ) - }); - - deprecated_lints.push(DeprecatedLint { - name: prefixed_name, - reason: reason.into(), - }); - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - println!("note: you must run `cargo uitest` to update the test results"); - } else { - eprintln!("error: lint not found"); - } -} - -fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { - fn remove_lint(name: &str, lints: &mut Vec) { - lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); - } - - fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{name}"); - let path = Path::new(&test_file_stem); - - // Some lints have their own directories, delete them - if path.is_dir() { - let _ = fs::remove_dir_all(path); - return; - } - - // Remove all related test files - let _ = fs::remove_file(path.with_extension("rs")); - let _ = fs::remove_file(path.with_extension("stderr")); - let _ = fs::remove_file(path.with_extension("fixed")); - } - - fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { - let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { - content - .find("declare_lint_pass!") - .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) - }); - let mut impl_lint_pass_end = content[impl_lint_pass_start..] - .find(']') - .expect("failed to find `impl_lint_pass` terminator"); - - impl_lint_pass_end += impl_lint_pass_start; - if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { - let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); - for c in content[lint_name_end..impl_lint_pass_end].chars() { - // Remove trailing whitespace - if c == ',' || c.is_whitespace() { - lint_name_end += 1; - } else { - break; + ( + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), + ), + ( + "CHANGELOG.md", + &mut update_text_region_fn( + "\n", + "", + |dst| { + for lint in lints + .iter() + .map(|l| &*l.name) + .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) + .sorted() + { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); + } + }, + ), + ), + ( + "clippy_lints/src/lib.rs", + &mut update_text_region_fn( + "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", + "// end lints modules, do not remove this comment, it’s used in `update_lints`", + |dst| { + for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() { + writeln!(dst, "mod {lint_mod};").unwrap(); + } + }, + ), + ), + ("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); + for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { + writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap(); } - } - - content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); - } - } - - if path.exists() - && let Some(lint) = lints.iter().find(|l| l.name == name) - { - if lint.module == name { - // The lint name is the same as the file, we can just delete the entire file - fs::remove_file(path)?; - } else { - // We can't delete the entire file, just remove the declaration - - if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { - // Remove clippy_lints/src/some_mod/some_lint.rs - let mut lint_mod_path = path.to_path_buf(); - lint_mod_path.set_file_name(name); - lint_mod_path.set_extension("rs"); - - let _ = fs::remove_file(lint_mod_path); - } - - let mut content = - fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); - - eprintln!( - "warn: you will have to manually remove any code related to `{name}` from `{}`", - path.display() - ); - - assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), - "error: `{}` does not contain lint `{}`'s declaration", - path.display(), - lint.name - ); - - // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range.clone(), ""); - - // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {name};"); - content = content.replacen(&mod_decl, "", 1); - - remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); - fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); - } - - remove_test_assets(name); - remove_lint(name, lints); - return Ok(true); - } - - Ok(false) -} - -/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there -/// were no replacements. -fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { - fn is_ident_char(c: u8) -> bool { - matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') - } - - let searcher = AhoCorasickBuilder::new() - .match_kind(aho_corasick::MatchKind::LeftmostLongest) - .build(replacements.iter().map(|&(x, _)| x.as_bytes())) - .unwrap(); - - let mut result = String::with_capacity(contents.len() + 1024); - let mut pos = 0; - let mut edited = false; - for m in searcher.find_iter(contents) { - let (old, new) = replacements[m.pattern()]; - result.push_str(&contents[pos..m.start()]); - result.push_str( - if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - edited = true; - new - } else { - old - }, - ); - pos = m.end(); - } - result.push_str(&contents[pos..]); - edited.then_some(result) + dst.push_str("];\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/deprecated.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + for lint in deprecated { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(renamed)), + ], + ); } fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } -fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) { - if update_mode == UpdateMode::Check { - let old_content = - fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display())); - if content != old_content { - exit_with_failure(); - } - } else { - fs::write(&path, content.as_bytes()) - .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display())); - } -} - /// Lint data parsed from the Clippy source code. #[derive(Clone, PartialEq, Eq, Debug)] -struct Lint { - name: String, - group: String, - desc: String, - module: String, - declaration_range: Range, -} - -impl Lint { - #[must_use] - fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range) -> Self { - Self { - name: name.to_lowercase(), - group: group.into(), - desc: remove_line_splices(desc), - module: module.into(), - declaration_range, - } - } - - /// Returns the lints in a `HashMap`, grouped by the different lint groups - #[must_use] - fn by_lint_group(lints: impl Iterator) -> HashMap> { - lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() - } +pub struct Lint { + pub name: String, + pub group: String, + pub module: String, + pub declaration_range: Range, } #[derive(Clone, PartialEq, Eq, Debug)] -struct DeprecatedLint { - name: String, - reason: String, +pub struct DeprecatedLint { + pub name: String, + pub reason: String, } -impl DeprecatedLint { - fn new(name: &str, reason: &str) -> Self { - Self { - name: remove_line_splices(name), - reason: remove_line_splices(reason), + +pub struct RenamedLint { + pub old_name: String, + pub new_name: String, +} + +pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { + move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for lint in lints { + if seen_lints.insert(&lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } } + seen_lints.clear(); + for lint in lints { + if seen_lints.insert(&lint.old_name) { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + } + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) } } -struct RenamedLint { - old_name: String, - new_name: String, -} -impl RenamedLint { - fn new(old_name: &str, new_name: &str) -> Self { - Self { - old_name: remove_line_splices(old_name), - new_name: remove_line_splices(new_name), - } - } -} - -/// Generates the code for registering lints +/// Finds all lint declarations (`declare_clippy_lint!`) #[must_use] -fn gen_declared_lints<'a>(lints: impl Iterator) -> String { - let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); - details.sort_unstable(); - - let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); - - for (module_name, lint_name) in details { - let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); - } - output.push_str("];\n"); - - output -} - -fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { - let mut res: String = GENERATED_FILE_COMMENT.into(); - for lint in lints { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); - } - res.push_str("\nfn main() {}\n"); - res -} - -fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { - let mut seen_lints = HashSet::new(); - let mut res: String = GENERATED_FILE_COMMENT.into(); - - res.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in lints { - if seen_lints.insert(&lint.new_name) { - writeln!(res, "#![allow({})]", lint.new_name).unwrap(); - } - } - seen_lints.clear(); - for lint in lints { - if seen_lints.insert(&lint.old_name) { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); - } - } - res.push_str("\nfn main() {}\n"); - res -} - -/// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec, Vec, Vec) { +pub fn find_lint_decls() -> Vec { let mut lints = Vec::with_capacity(1000); - let mut deprecated_lints = Vec::with_capacity(50); - let mut renamed_lints = Vec::with_capacity(50); + let mut contents = String::new(); + for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { + parse_clippy_lint_decls( + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); + } + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints +} - for (rel_path, file) in clippy_lints_src_files() { - let path = file.path(); - let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::>() - .join("::"); - - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - let module = if let Some(module) = module.strip_suffix("::mod.rs") { - module - } else { - module.strip_suffix(".rs").unwrap_or(&module) +/// Reads the source files from the given root directory +fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator { + WalkDir::new(src_root).into_iter().filter_map(move |e| { + let e = match e { + Ok(e) => e, + Err(ref e) => panic_file(e, FileAction::Read, src_root), }; - - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); + let path = e.path().as_os_str().as_encoded_bytes(); + if let Some(path) = path.strip_suffix(b".rs") + && let Some(path) = path.get("clippy_lints/src/".len()..) + { + if path == b"lib" { + Some((e, String::new())) + } else { + let path = if let Some(path) = path.strip_suffix(b"mod") + && let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\")) + { + path + } else { + path + }; + if let Ok(path) = str::from_utf8(path) { + let path = path.replace(['/', '\\'], "::"); + Some((e, path)) + } else { + None + } + } } else { - parse_contents(&contents, module, &mut lints); + None } - } - (lints, deprecated_lints, renamed_lints) -} - -fn clippy_lints_src_files() -> impl Iterator { - let root_path = clippy_project_root().join("clippy_lints/src"); - let iter = WalkDir::new(&root_path).into_iter(); - iter.map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) -} - -macro_rules! match_tokens { - ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { - { - $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult { - token_kind: TokenKind::$token $({$($fields)*})?, - content: $($capture @)? _, - .. - }) = $iter.next() else { - continue; - };)* - #[allow(clippy::unused_unit)] - { ($($($capture,)?)*) } - } - } -} - -pub(crate) use match_tokens; - -pub(crate) struct LintDeclSearchResult<'a> { - pub token_kind: TokenKind, - pub content: &'a str, - pub range: Range, + }) } /// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { - let mut offset = 0usize; - let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; +fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec) { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token] = &[ + // !{ /// docs + Bang, OpenBrace, AnyDoc, + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // pub NAME, GROUP, + Ident("pub"), CaptureIdent, Comma, CaptureIdent, Comma, + ]; - LintDeclSearchResult { - token_kind: t.kind, - content: &contents[range.clone()], - range, - } - }); - - while let Some(LintDeclSearchResult { range, .. }) = iter.find( - |LintDeclSearchResult { - token_kind, content, .. - }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint", - ) { - let start = range.start; - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - match iter.next() { - // #[clippy::version = "version"] pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Pound, - .. - }) => { - match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); - }, - // pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - .. - }) => (), - _ => continue, - } - - let (name, group, desc) = match_tokens!( - iter, - // LINT_NAME - Ident(name) Comma - // group, - Ident(group) Comma - // "description" - Literal{..}(desc) - ); - - if let Some(end) = iter.find_map(|t| { - if let LintDeclSearchResult { - token_kind: TokenKind::CloseBrace, - range, - .. - } = t - { - Some(range.end) - } else { - None - } - }) { - lints.push(Lint::new(name, group, desc, module, start..end)); + let mut searcher = RustSearcher::new(contents); + while searcher.find_token(Ident("declare_clippy_lint")) { + let start = searcher.pos() as usize - "declare_clippy_lint".len(); + let (mut name, mut group) = ("", ""); + if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) { + lints.push(Lint { + name: name.to_lowercase(), + group: group.into(), + module: module.into(), + declaration_range: start..searcher.pos() as usize, + }); } } } -/// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec, renamed: &mut Vec) { - let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { - return; - }; - let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { - return; +pub struct DeprecatedLints { + pub file: File<'static>, + pub contents: String, + pub deprecated: Vec, + pub renamed: Vec, + pub deprecated_end: u32, + pub renamed_end: u32, +} + +#[must_use] +pub fn read_deprecated_lints() -> DeprecatedLints { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token] = &[ + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // ("first", "second"), + OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, + ]; + #[rustfmt::skip] + static DEPRECATED_TOKENS: &[Token] = &[ + // !{ DEPRECATED(DEPRECATED_VERSION) = [ + Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + #[rustfmt::skip] + static RENAMED_TOKENS: &[Token] = &[ + // !{ RENAMED(RENAMED_VERSION) = [ + Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + + let path = "clippy_lints/src/deprecated_lints.rs"; + let mut res = DeprecatedLints { + file: File::open(path, OpenOptions::new().read(true).write(true)), + contents: String::new(), + deprecated: Vec::with_capacity(30), + renamed: Vec::with_capacity(80), + deprecated_end: 0, + renamed_end: 0, }; - for line in deprecated_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; + res.file.read_append_to_string(&mut res.contents); + let mut searcher = RustSearcher::new(&res.contents); - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); + // First instance is the macro definition. + assert!( + searcher.find_token(Ident("declare_with_version")), + "error reading deprecated lints" + ); - let (name, reason) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma - ); - deprecated.push(DeprecatedLint::new(name, reason)); + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + let mut name = ""; + let mut reason = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut reason]) { + res.deprecated.push(DeprecatedLint { + name: parse_str_single_line(path.as_ref(), name), + reason: parse_str_single_line(path.as_ref(), reason), + }); + } + } else { + panic!("error reading deprecated lints"); } - for line in renamed_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; + // position of the closing `]}` of `declare_with_version` + res.deprecated_end = searcher.pos(); - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); - - let (old_name, new_name) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma - ); - renamed.push(RenamedLint::new(old_name, new_name)); + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + let mut old_name = ""; + let mut new_name = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut old_name, &mut new_name]) { + res.renamed.push(RenamedLint { + old_name: parse_str_single_line(path.as_ref(), old_name), + new_name: parse_str_single_line(path.as_ref(), new_name), + }); + } + } else { + panic!("error reading renamed lints"); } + // position of the closing `]}` of `declare_with_version` + res.renamed_end = searcher.pos(); + + res } /// Removes the line splices and surrounding quotes from a string literal -fn remove_line_splices(s: &str) -> String { +fn parse_str_lit(s: &str) -> String { + let (s, mode) = if let Some(s) = s.strip_prefix("r") { + (s.trim_matches('#'), rustc_literal_escaper::Mode::RawStr) + } else { + (s, rustc_literal_escaper::Mode::Str) + }; let s = s - .strip_prefix('r') - .unwrap_or(s) - .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); - unescape_unicode(s, Mode::Str, &mut |range, ch| { - if ch.is_ok() { - res.push_str(&s[range]); + rustc_literal_escaper::unescape_unicode(s, mode, &mut |_, ch| { + if let Ok(ch) = ch { + res.push(ch); } }); res } -fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { - match OpenOptions::new().create_new(true).write(true).open(new_name) { - Ok(file) => drop(file), - Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_file(e, new_name, "create"), - } - match fs::rename(old_name, new_name) { - Ok(()) => true, - Err(e) => { - drop(fs::remove_file(new_name)); - if e.kind() == io::ErrorKind::NotFound { - false - } else { - panic_file(e, old_name, "rename"); - } - }, - } -} -#[allow(clippy::needless_pass_by_value)] -fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { - panic!("failed to {action} file `{}`: {error}", name.display()) -} - -fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option { - let i = text.find(marker)?; - let (pre, post) = text.split_at(i); - Some([pre, new_text, post].into_iter().collect()) -} - -fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { - let mut file = OpenOptions::new() - .write(true) - .read(true) - .open(path) - .unwrap_or_else(|e| panic_file(e, path, "open")); - let mut buf = String::new(); - file.read_to_string(&mut buf) - .unwrap_or_else(|e| panic_file(e, path, "read")); - if let Some(new_contents) = f(&buf) { - file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); - file.write_all(new_contents.as_bytes()) - .unwrap_or_else(|e| panic_file(e, path, "write")); - file.set_len(new_contents.len() as u64) - .unwrap_or_else(|e| panic_file(e, path, "write")); - } -} - -fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); +fn parse_str_single_line(path: &Path, s: &str) -> String { + let value = parse_str_lit(s); + assert!( + !value.contains('\n'), + "error parsing `{}`: `{s}` should be a single line string", + path.display(), + ); + value } #[cfg(test)] @@ -868,7 +348,7 @@ mod tests { use super::*; #[test] - fn test_parse_contents() { + fn test_parse_clippy_lint_decls() { static CONTENTS: &str = r#" declare_clippy_lint! { #[clippy::version = "Hello Clippy!"] @@ -886,61 +366,25 @@ mod tests { } "#; let mut result = Vec::new(); - parse_contents(CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls(CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } let expected = vec![ - Lint::new( - "ptr_arg", - "style", - "\"really long text\"", - "module_name", - Range::default(), - ), - Lint::new( - "doc_markdown", - "pedantic", - "\"single line\"", - "module_name", - Range::default(), - ), + Lint { + name: "ptr_arg".into(), + group: "style".into(), + module: "module_name".into(), + declaration_range: Range::default(), + }, + Lint { + name: "doc_markdown".into(), + group: "pedantic".into(), + module: "module_name".into(), + declaration_range: Range::default(), + }, ]; assert_eq!(expected, result); } - - #[test] - fn test_by_lint_group() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ]; - let mut expected: HashMap> = HashMap::new(); - expected.insert( - "group1".to_string(), - vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ], - ); - expected.insert( - "group2".to_string(), - vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )], - ); - assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); - } } diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index 206816398f50..ae2eabc45dd0 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -1,12 +1,113 @@ +use aho_corasick::{AhoCorasick, AhoCorasickBuilder}; +use core::fmt::{self, Display}; +use core::slice; +use core::str::FromStr; +use rustc_lexer::{self as lexer, FrontmatterAllowed}; +use std::env; +use std::fs::{self, OpenOptions}; +use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::process::{self, ExitStatus}; -use std::{fs, io}; #[cfg(not(windows))] static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; #[cfg(windows)] static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; +#[derive(Clone, Copy)] +pub enum FileAction { + Open, + Read, + Write, + Create, + Rename, +} +impl FileAction { + fn as_str(self) -> &'static str { + match self { + Self::Open => "opening", + Self::Read => "reading", + Self::Write => "writing", + Self::Create => "creating", + Self::Rename => "renaming", + } + } +} + +#[cold] +#[track_caller] +pub fn panic_file(err: &impl Display, action: FileAction, path: &Path) -> ! { + panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) +} + +/// Wrapper around `std::fs::File` which panics with a path on failure. +pub struct File<'a> { + pub inner: fs::File, + pub path: &'a Path, +} +impl<'a> File<'a> { + /// Opens a file panicking on failure. + #[track_caller] + pub fn open(path: &'a (impl AsRef + ?Sized), options: &mut OpenOptions) -> Self { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Self { inner, path }, + Err(e) => panic_file(&e, FileAction::Open, path), + } + } + + /// Opens a file if it exists, panicking on any other failure. + #[track_caller] + pub fn open_if_exists(path: &'a (impl AsRef + ?Sized), options: &mut OpenOptions) -> Option { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Some(Self { inner, path }), + Err(e) if e.kind() == io::ErrorKind::NotFound => None, + Err(e) => panic_file(&e, FileAction::Open, path), + } + } + + /// Opens and reads a file into a string, panicking of failure. + #[track_caller] + pub fn open_read_to_cleared_string<'dst>( + path: &'a (impl AsRef + ?Sized), + dst: &'dst mut String, + ) -> &'dst mut String { + Self::open(path, OpenOptions::new().read(true)).read_to_cleared_string(dst) + } + + /// Read the entire contents of a file to the given buffer. + #[track_caller] + pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + match self.inner.read_to_string(dst) { + Ok(_) => {}, + Err(e) => panic_file(&e, FileAction::Read, self.path), + } + dst + } + + #[track_caller] + pub fn read_to_cleared_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + dst.clear(); + self.read_append_to_string(dst) + } + + /// Replaces the entire contents of a file. + #[track_caller] + pub fn replace_contents(&mut self, data: &[u8]) { + let res = match self.inner.seek(SeekFrom::Start(0)) { + Ok(_) => match self.inner.write_all(data) { + Ok(()) => self.inner.set_len(data.len() as u64), + Err(e) => Err(e), + }, + Err(e) => Err(e), + }; + if let Err(e) = res { + panic_file(&e, FileAction::Write, self.path); + } + } +} + /// Returns the path to the `cargo-clippy` binary /// /// # Panics @@ -14,34 +115,107 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; /// Panics if the path of current executable could not be retrieved. #[must_use] pub fn cargo_clippy_path() -> PathBuf { - let mut path = std::env::current_exe().expect("failed to get current executable name"); + let mut path = env::current_exe().expect("failed to get current executable name"); path.set_file_name(CARGO_CLIPPY_EXE); path } -/// 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 = fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result - && err.kind() == io::ErrorKind::NotFound +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + pub major: u16, + pub minor: u16, +} +impl FromStr for Version { + type Err = (); + fn from_str(s: &str) -> Result { + if let Some(s) = s.strip_prefix("0.") + && let Some((major, minor)) = s.split_once('.') + && let Ok(major) = major.parse() + && let Ok(minor) = minor.parse() { - continue; - } - - let content = result.unwrap(); - if content.contains("[package]\nname = \"clippy\"") { - return path.to_path_buf(); + Ok(Self { major, minor }) + } else { + Err(()) + } + } +} +impl Version { + /// Displays the version as a rust version. i.e. `x.y.0` + #[must_use] + pub fn rust_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}.0", self.0.major, self.0.minor) + } + } + X(self) + } + + /// Displays the version as it should appear in clippy's toml files. i.e. `0.x.y` + #[must_use] + pub fn toml_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0.{}.{}", self.0.major, self.0.minor) + } + } + X(self) + } +} + +pub struct ClippyInfo { + pub path: PathBuf, + pub version: Version, +} +impl ClippyInfo { + #[must_use] + pub fn search_for_manifest() -> Self { + let mut path = env::current_dir().expect("error reading the working directory"); + let mut buf = String::new(); + loop { + path.push("Cargo.toml"); + if let Some(mut file) = File::open_if_exists(&path, OpenOptions::new().read(true)) { + let mut in_package = false; + let mut is_clippy = false; + let mut version: Option = None; + + // Ad-hoc parsing to avoid dependencies. We control all the file so this + // isn't actually a problem + for line in file.read_to_cleared_string(&mut buf).lines() { + if line.starts_with('[') { + in_package = line.starts_with("[package]"); + } else if in_package && let Some((name, value)) = line.split_once('=') { + match name.trim() { + "name" => is_clippy = value.trim() == "\"clippy\"", + "version" + if let Some(value) = value.trim().strip_prefix('"') + && let Some(value) = value.strip_suffix('"') => + { + version = value.parse().ok(); + }, + _ => {}, + } + } + } + + if is_clippy { + let Some(version) = version else { + panic!("error reading clippy version from {}", file.path.display()); + }; + path.pop(); + return ClippyInfo { path, version }; + } + } + + path.pop(); + assert!( + path.pop(), + "error finding project root, please run from inside the clippy directory" + ); } } - panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); } /// # Panics @@ -57,86 +231,396 @@ pub fn exit_if_err(status: io::Result) { } } -pub(crate) fn clippy_version() -> (u32, u32) { - fn parse_manifest(contents: &str) -> Option<(u32, u32)> { - let version = contents - .lines() - .filter_map(|l| l.split_once('=')) - .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?; - let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else { - return None; - }; - let (minor, patch) = version.split_once('.')?; - Some((minor.parse().ok()?, patch.parse().ok()?)) +#[derive(Clone, Copy)] +pub enum UpdateStatus { + Unchanged, + Changed, +} +impl UpdateStatus { + #[must_use] + pub fn from_changed(value: bool) -> Self { + if value { Self::Changed } else { Self::Unchanged } + } + + #[must_use] + pub fn is_changed(self) -> bool { + matches!(self, Self::Changed) } - let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`"); - parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy)] pub enum UpdateMode { - Check, Change, + Check, +} +impl UpdateMode { + #[must_use] + pub fn from_check(check: bool) -> Self { + if check { Self::Check } else { Self::Change } + } } -pub(crate) fn exit_with_failure() { - println!( - "Not all lints defined properly. \ - Please run `cargo dev update_lints` to make sure all lints are defined properly." - ); - process::exit(1); +#[derive(Default)] +pub struct FileUpdater { + src_buf: String, + dst_buf: String, } +impl FileUpdater { + fn update_file_checked_inner( + &mut self, + tool: &str, + mode: UpdateMode, + path: &Path, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + match (mode, update(path, &self.src_buf, &mut self.dst_buf)) { + (UpdateMode::Check, UpdateStatus::Changed) => { + eprintln!( + "the contents of `{}` are out of date\nplease run `{tool}` to update", + path.display() + ); + process::exit(1); + }, + (UpdateMode::Change, UpdateStatus::Changed) => file.replace_contents(self.dst_buf.as_bytes()), + (UpdateMode::Check | UpdateMode::Change, UpdateStatus::Unchanged) => {}, + } + } -/// Replaces a region in a file delimited by two lines matching regexes. -/// -/// `path` is the relative path to the file on which you want to perform the replacement. -/// -/// See `replace_region_in_text` for documentation of the other options. -/// -/// # Panics -/// -/// Panics if the path could not read or then written -pub(crate) fn replace_region_in_file( - update_mode: UpdateMode, - path: &Path, - start: &str, - end: &str, - write_replacement: impl FnMut(&mut String), -) { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { - Ok(x) => x, - Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()), - }; + fn update_file_inner(&mut self, path: &Path, update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + if update(path, &self.src_buf, &mut self.dst_buf).is_changed() { + file.replace_contents(self.dst_buf.as_bytes()); + } + } - match update_mode { - UpdateMode::Check if contents != new_contents => exit_with_failure(), - UpdateMode::Check => (), - UpdateMode::Change => { - if let Err(e) = fs::write(path, new_contents.as_bytes()) { - panic!("Cannot write to `{}`: {e}", path.display()); - } - }, + pub fn update_file_checked( + &mut self, + tool: &str, + mode: UpdateMode, + path: impl AsRef, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + + #[expect(clippy::type_complexity)] + pub fn update_files_checked( + &mut self, + tool: &str, + mode: UpdateMode, + files: &mut [( + impl AsRef, + &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + )], + ) { + for (path, update) in files { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + } + + pub fn update_file( + &mut self, + path: impl AsRef, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_inner(path.as_ref(), update); } } /// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters /// were found, or the missing delimiter if not. -pub(crate) fn replace_region_in_text<'a>( - text: &str, - start: &'a str, - end: &'a str, - mut write_replacement: impl FnMut(&mut String), -) -> Result { - let (text_start, rest) = text.split_once(start).ok_or(start)?; - let (_, text_end) = rest.split_once(end).ok_or(end)?; - - let mut res = String::with_capacity(text.len() + 4096); - res.push_str(text_start); - res.push_str(start); - write_replacement(&mut res); - res.push_str(end); - res.push_str(text_end); - - Ok(res) +pub fn update_text_region( + path: &Path, + start: &str, + end: &str, + src: &str, + dst: &mut String, + insert: &mut impl FnMut(&mut String), +) -> UpdateStatus { + let Some((src_start, src_end)) = src.split_once(start) else { + panic!("`{}` does not contain `{start}`", path.display()); + }; + let Some((replaced_text, src_end)) = src_end.split_once(end) else { + panic!("`{}` does not contain `{end}`", path.display()); + }; + dst.push_str(src_start); + dst.push_str(start); + let new_start = dst.len(); + insert(dst); + let changed = dst[new_start..] != *replaced_text; + dst.push_str(end); + dst.push_str(src_end); + UpdateStatus::from_changed(changed) +} + +pub fn update_text_region_fn( + start: &str, + end: &str, + mut insert: impl FnMut(&mut String), +) -> impl FnMut(&Path, &str, &mut String) -> UpdateStatus { + move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) +} + +#[must_use] +pub fn is_ident_char(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') +} + +pub struct StringReplacer<'a> { + searcher: AhoCorasick, + replacements: &'a [(&'a str, &'a str)], +} +impl<'a> StringReplacer<'a> { + #[must_use] + pub fn new(replacements: &'a [(&'a str, &'a str)]) -> Self { + Self { + searcher: AhoCorasickBuilder::new() + .match_kind(aho_corasick::MatchKind::LeftmostLongest) + .build(replacements.iter().map(|&(x, _)| x)) + .unwrap(), + replacements, + } + } + + /// Replace substrings if they aren't bordered by identifier characters. + pub fn replace_ident_fn(&self) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { + move |_, src, dst| { + let mut pos = 0; + let mut changed = false; + for m in self.searcher.find_iter(src) { + if !is_ident_char(src.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(src.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + changed = true; + dst.push_str(&src[pos..m.start()]); + dst.push_str(self.replacements[m.pattern()].1); + pos = m.end(); + } + } + dst.push_str(&src[pos..]); + UpdateStatus::from_changed(changed) + } + } +} + +#[derive(Clone, Copy)] +pub enum Token { + /// Matches any number of doc comments. + AnyDoc, + Ident(&'static str), + CaptureIdent, + LitStr, + CaptureLitStr, + Bang, + CloseBrace, + CloseBracket, + CloseParen, + /// This will consume the first colon even if the second doesn't exist. + DoubleColon, + Comma, + Eq, + Lifetime, + Lt, + Gt, + OpenBrace, + OpenBracket, + OpenParen, + Pound, +} + +pub struct RustSearcher<'txt> { + text: &'txt str, + cursor: lexer::Cursor<'txt>, + pos: u32, + + // Either the next token or a zero-sized whitespace sentinel. + next_token: lexer::Token, +} +impl<'txt> RustSearcher<'txt> { + #[must_use] + pub fn new(text: &'txt str) -> Self { + Self { + text, + cursor: lexer::Cursor::new(text, FrontmatterAllowed::Yes), + pos: 0, + + // Sentinel value indicating there is no read token. + next_token: lexer::Token { + len: 0, + kind: lexer::TokenKind::Whitespace, + }, + } + } + + #[must_use] + pub fn peek_text(&self) -> &'txt str { + &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] + } + + #[must_use] + pub fn peek(&self) -> lexer::TokenKind { + self.next_token.kind + } + + #[must_use] + pub fn pos(&self) -> u32 { + self.pos + } + + #[must_use] + pub fn at_end(&self) -> bool { + self.next_token.kind == lexer::TokenKind::Eof + } + + pub fn step(&mut self) { + // `next_len` is zero for the sentinel value and the eof marker. + self.pos += self.next_token.len; + self.next_token = self.cursor.advance_token(); + } + + /// Consumes the next token if it matches the requested value and captures the value if + /// requested. Returns true if a token was matched. + fn read_token(&mut self, token: Token, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + loop { + match (token, self.next_token.kind) { + // Has to be the first match arm so the empty sentinel token will be handled. + // This will also skip all whitespace/comments preceding any tokens. + ( + _, + lexer::TokenKind::Whitespace + | lexer::TokenKind::LineComment { doc_style: None } + | lexer::TokenKind::BlockComment { + doc_style: None, + terminated: true, + }, + ) => { + self.step(); + if self.at_end() { + // `AnyDoc` always matches. + return matches!(token, Token::AnyDoc); + } + }, + ( + Token::AnyDoc, + lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, + ) => { + self.step(); + if self.at_end() { + // `AnyDoc` always matches. + return true; + } + }, + (Token::AnyDoc, _) => return true, + (Token::Bang, lexer::TokenKind::Bang) + | (Token::CloseBrace, lexer::TokenKind::CloseBrace) + | (Token::CloseBracket, lexer::TokenKind::CloseBracket) + | (Token::CloseParen, lexer::TokenKind::CloseParen) + | (Token::Comma, lexer::TokenKind::Comma) + | (Token::Eq, lexer::TokenKind::Eq) + | (Token::Lifetime, lexer::TokenKind::Lifetime { .. }) + | (Token::Lt, lexer::TokenKind::Lt) + | (Token::Gt, lexer::TokenKind::Gt) + | (Token::OpenBrace, lexer::TokenKind::OpenBrace) + | (Token::OpenBracket, lexer::TokenKind::OpenBracket) + | (Token::OpenParen, lexer::TokenKind::OpenParen) + | (Token::Pound, lexer::TokenKind::Pound) + | ( + Token::LitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) => { + self.step(); + return true; + }, + (Token::Ident(x), lexer::TokenKind::Ident) if x == self.peek_text() => { + self.step(); + return true; + }, + (Token::DoubleColon, lexer::TokenKind::Colon) => { + self.step(); + if !self.at_end() && matches!(self.next_token.kind, lexer::TokenKind::Colon) { + self.step(); + return true; + } + return false; + }, + ( + Token::CaptureLitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) + | (Token::CaptureIdent, lexer::TokenKind::Ident) => { + **captures.next().unwrap() = self.peek_text(); + self.step(); + return true; + }, + _ => return false, + } + } + } + + #[must_use] + pub fn find_token(&mut self, token: Token) -> bool { + let mut capture = [].iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return false; + } + } + true + } + + #[must_use] + pub fn find_capture_token(&mut self, token: Token) -> Option<&'txt str> { + let mut res = ""; + let mut capture = &mut res; + let mut capture = slice::from_mut(&mut capture).iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return None; + } + } + Some(res) + } + + #[must_use] + pub fn match_tokens(&mut self, tokens: &[Token], captures: &mut [&mut &'txt str]) -> bool { + let mut captures = captures.iter_mut(); + tokens.iter().all(|&t| self.read_token(t, &mut captures)) + } +} + +#[expect(clippy::must_use_candidate)] +pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(e) => panic_file(&e, FileAction::Create, new_name), + } + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(e) => { + drop(fs::remove_file(new_name)); + if e.kind() == io::ErrorKind::NotFound { + false + } else { + panic_file(&e, FileAction::Rename, old_name); + } + }, + } +} + +pub fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_file(&e, FileAction::Write, path)); } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 20951afccbb7..7e3cb4042479 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index f7f168cb2679..9a1242980418 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -468,7 +468,7 @@ declare_clippy_lint! { /// #[ignore = "Some good reason"] /// fn test() {} /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.88.0"] pub IGNORE_WITHOUT_REASON, pedantic, "ignored tests without messages" diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index d75b73280e63..4059f9603c33 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -26,16 +26,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if namespace.is_none() && matches!( - name.as_str(), - "ambiguous_glob_reexports" - | "dead_code" - | "deprecated" - | "hidden_glob_reexports" - | "unreachable_pub" - | "unused" - | "unused_braces" - | "unused_import_braces" - | "unused_imports" + name, + sym::ambiguous_glob_reexports + | sym::dead_code + | sym::deprecated + | sym::hidden_glob_reexports + | sym::unreachable_pub + | sym::unused + | sym::unused_braces + | sym::unused_import_braces + | sym::unused_imports ) { return; @@ -43,16 +43,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if namespace == Some(sym::clippy) && matches!( - name.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - | "disallowed_types" - | "unused_trait_names" + name, + sym::wildcard_imports + | sym::enum_glob_use + | sym::redundant_pub_crate + | sym::macro_use_imports + | sym::unsafe_removed_from_name + | sym::module_name_repetitions + | sym::single_component_path_imports + | sym::disallowed_types + | sym::unused_trait_names ) { return; 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 52d1d5b4c67a..31cc004f6855 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::paths::{self, PathNS}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; @@ -182,6 +182,7 @@ impl AwaitHolding { let (def_ids, _) = create_disallowed_map( tcx, &conf.await_holding_invalid_types, + PathNS::Type, crate::disallowed_types::def_kind_predicate, "type", false, @@ -275,12 +276,10 @@ fn emit_invalid_type( } fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) + match cx.tcx.get_diagnostic_name(def_id) { + Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard), + None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)), + } } fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index 4a876b854165..ae36bb76117d 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -73,10 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); - let eq_macro = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => true, - "assert_ne" | "debug_assert_ne" => false, + let eq_macro = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_eq_macro | sym::debug_assert_eq_macro) => true, + Some(sym::assert_ne_macro | sym::debug_assert_ne_macro) => false, _ => return, }; let Some((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -115,6 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { return; } + let macro_name = cx.tcx.item_name(macro_call.def_id); let macro_name = macro_name.as_str(); let non_eq_mac = ¯o_name[..macro_name.len() - 3]; span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs new file mode 100644 index 000000000000..31cdd078f45a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::{Symbol, sym}; + +use super::CONFUSING_METHOD_TO_NUMERIC_CAST; + +fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Char => Some("char"), + ty::Int(int) => Some(int.name_str()), + ty::Uint(uint) => Some(uint.name_str()), + ty::Float(float) => Some(float.name_str()), + _ => None, + } +} + +fn get_const_name_and_ty_name( + cx: &LateContext<'_>, + method_name: Symbol, + method_def_id: DefId, + generics: &[GenericArg<'_>], +) -> Option<(&'static str, &'static str)> { + let method_name = method_name.as_str(); + let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id); + + let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) { + // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. + if let [ty] = generics + && let Some(ty) = ty.as_type() + { + get_primitive_ty_name(ty)? + } else { + return None; + } + } else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id) + && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) + && ["min", "max", "minimum", "maximum", "min_value", "max_value"].contains(&method_name) + { + ty_name + } else { + return None; + }; + + let const_name = if method_name.starts_with("max") { "MAX" } else { "MIN" }; + Some((const_name, ty_name)) +} + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We allow casts from any function type to any function type. + match cast_to.kind() { + ty::FnDef(..) | ty::FnPtr(..) => return, + _ => { /* continue to checks */ }, + } + + if let ty::FnDef(def_id, generics) = cast_from.kind() + && let Some(method_name) = cx.tcx.opt_item_name(*def_id) + && let Some((const_name, ty_name)) = get_const_name_and_ty_name(cx, method_name, *def_id, generics.as_slice()) + { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + + span_lint_and_then( + cx, + CONFUSING_METHOD_TO_NUMERIC_CAST, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_verbose( + expr.span, + "did you mean to use the associated constant?", + format!("{ty_name}::{const_name} as {cast_to}"), + applicability, + ); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index 8ace27eca895..61dfc0fc0425 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_normalizable; -use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core}; +use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; @@ -55,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind && let Some(fun_id) = path_def_id(cx, fun) - && match_def_path(cx, fun_id, &paths::ALIGN_OF) + && paths::ALIGN_OF.matches(cx, fun_id) && let Some(args) = path.segments.last().and_then(|seg| seg.args) && let [GenericArg::Type(generic_ty)] = args.args { @@ -71,12 +70,10 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) return false; } let to_mid_ty = cx.typeck_results().node_type(to.hir_id); - is_normalizable(cx, cx.param_env, to_mid_ty) - && cx - .tcx - .layout_of(cx.typing_env().as_query_input(to_mid_ty)) - .is_ok_and(|layout| { - let align = u128::from(layout.align.abi.bytes()); - u128::from(val) <= align - }) + cx.tcx + .layout_of(cx.typing_env().as_query_input(to_mid_ty)) + .is_ok_and(|layout| { + let align = u128::from(layout.align.abi.bytes()); + u128::from(val) <= align + }) } diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 76931fce209e..daae9a8bb085 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -14,6 +14,7 @@ mod cast_sign_loss; mod cast_slice_different_sizes; mod cast_slice_from_raw_parts; mod char_lit_as_u8; +mod confusing_method_to_numeric_cast; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; @@ -780,12 +781,38 @@ declare_clippy_lint! { /// let aligned = std::ptr::dangling::(); /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); /// ``` - #[clippy::version = "1.87.0"] + #[clippy::version = "1.88.0"] pub MANUAL_DANGLING_PTR, style, "casting small constant literals to pointers to create dangling pointers" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. + /// + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. + /// + /// ### Example + /// ```no_run + /// let _ = u16::max as usize; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let _ = u16::MAX as usize; + /// ``` + #[clippy::version = "1.86.0"] + pub CONFUSING_METHOD_TO_NUMERIC_CAST, + suspicious, + "casting a primitive method pointer to any integer type" +} + pub struct Casts { msrv: Msrv, } @@ -823,6 +850,7 @@ impl_lint_pass!(Casts => [ REF_AS_PTR, AS_POINTER_UNDERSCORE, MANUAL_DANGLING_PTR, + CONFUSING_METHOD_TO_NUMERIC_CAST, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -847,6 +875,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); + confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs new file mode 100644 index 000000000000..6b239a1541b0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -0,0 +1,100 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{is_in_const_context, is_mutable, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for slice references with cloned references such as `&[f.clone()]`. + /// + /// ### Why is this bad + /// + /// A reference does not need to be owned in order to used as a slice. + /// + /// ### Known problems + /// + /// This lint does not know whether or not a clone implementation has side effects. + /// + /// ### Example + /// + /// ```ignore + /// let data = 10; + /// let data_ref = &data; + /// take_slice(&[data_ref.clone()]); + /// ``` + /// Use instead: + /// ```ignore + /// use std::slice; + /// let data = 10; + /// let data_ref = &data; + /// take_slice(slice::from_ref(data_ref)); + /// ``` + #[clippy::version = "1.87.0"] + pub CLONED_REF_TO_SLICE_REFS, + perf, + "cloning a reference for slice references" +} + +pub struct ClonedRefToSliceRefs<'a> { + msrv: &'a Msrv, +} +impl<'a> ClonedRefToSliceRefs<'a> { + pub fn new(conf: &'a Conf) -> Self { + Self { msrv: &conf.msrv } + } +} + +impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); + +impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if self.msrv.meets(cx, { + if is_in_const_context(cx) { + msrvs::CONST_SLICE_FROM_REF + } else { + msrvs::SLICE_FROM_REF + } + }) + // `&[foo.clone()]` expressions + && let ExprKind::AddrOf(_, mutability, arr) = &expr.kind + // mutable references would have a different meaning + && mutability.is_not() + + // check for single item arrays + && let ExprKind::Array([item]) = &arr.kind + + // check for clones + && let ExprKind::MethodCall(_, val, _, _) = item.kind + && is_trait_method(cx, item, sym::Clone) + + // check for immutability or purity + && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) + + // get appropriate crate for `slice::from_ref` + && let Some(builtin_crate) = clippy_utils::std_or_core(cx) + { + let mut sugg = Sugg::hir(cx, val, "_"); + if !cx.typeck_results().expr_ty(val).is_ref() { + sugg = sugg.addr(); + } + + span_lint_and_sugg( + cx, + CLONED_REF_TO_SLICE_REFS, + expr.span, + format!("this call to `clone` can be replaced with `{builtin_crate}::slice::from_ref`"), + "try", + format!("{builtin_crate}::slice::from_ref({sugg})"), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 20fae8a6775b..7f6ecea99fb0 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -1,11 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; use rustc_ast::BinOpKind; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -78,14 +78,14 @@ declare_clippy_lint! { } pub struct CollapsibleIf { - let_chains_enabled: bool, + msrv: Msrv, lint_commented_code: bool, } impl CollapsibleIf { - pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - let_chains_enabled: tcx.features().let_chains(), + msrv: conf.msrv, lint_commented_code: conf.lint_commented_code, } } @@ -127,7 +127,7 @@ impl CollapsibleIf { if let Some(inner) = expr_block(then) && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind - && self.eligible_condition(check_inner) + && self.eligible_condition(cx, check_inner) && let ctxt = expr.span.ctxt() && inner.span.ctxt() == ctxt && (self.lint_commented_code || !block_starts_with_comment(cx, then)) @@ -163,8 +163,9 @@ impl CollapsibleIf { } } - pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool { - self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..)) + fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { + !matches!(cond.kind, ExprKind::Let(..)) + || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) } } @@ -180,7 +181,7 @@ impl LateLintPass<'_> for CollapsibleIf { { Self::check_collapsible_else_if(cx, then.span, else_); } else if else_.is_none() - && self.eligible_condition(cond) + && self.eligible_condition(cx, cond) && let ExprKind::Block(then, None) = then.kind { self.check_collapsible_if_if(cx, expr, cond, then); @@ -202,13 +203,12 @@ fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { match block.stmts { [] => block.expr, - [stmt] => { - if let StmtKind::Semi(expr) = stmt.kind { - Some(expr) - } else { - None - } - }, + [ + Stmt { + kind: StmtKind::Semi(expr), + .. + }, + ] if block.expr.is_none() => Some(expr), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 2cccd6ba2702..bb825c7655f8 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -64,6 +64,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, crate::casts::CHAR_LIT_AS_U8_INFO, + crate::casts::CONFUSING_METHOD_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, @@ -75,6 +76,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::ZERO_PTR_INFO, crate::cfg_not_test::CFG_NOT_TEST_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, + crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_IF_INFO, @@ -705,13 +707,9 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, - crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, - crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, - crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, - crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index b60c11d79d48..946515386690 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -2,18 +2,18 @@ // Prefer to use those when possible. macro_rules! declare_with_version { - ($name:ident($name_version:ident): &[$ty:ty] = &[$( + ($name:ident($name_version:ident) = [$( #[clippy::version = $version:literal] $e:expr, )*]) => { - pub static $name: &[$ty] = &[$($e),*]; + pub static $name: &[(&str, &str)] = &[$($e),*]; #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } #[rustfmt::skip] -declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ +declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] @@ -44,11 +44,10 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), #[clippy::version = "1.86.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), - // end deprecated lints. used by `cargo dev deprecate_lint` ]} #[rustfmt::skip] -declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ +declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), #[clippy::version = ""] @@ -187,5 +186,12 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), #[clippy::version = ""] ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), - // end renamed lints. used by `cargo dev rename_lint` + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_float", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_char", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_float_to_int", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), ]} diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 06528f875a29..3443b36eb4f3 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; +use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; @@ -377,7 +377,7 @@ fn check_unsafe_derive_deserialize<'tcx>( } if let Some(trait_def_id) = trait_ref.trait_def_id() - && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE) + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() && let Some(local_def_id) = def.did().as_local() && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index fc6af204a74a..9814d4fa84f9 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -3,6 +3,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; @@ -76,6 +77,7 @@ impl DisallowedMacros { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_macros, + PathNS::Macro, |def_kind| matches!(def_kind, DefKind::Macro(_)), "macro", false, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 1382dafa931e..fb970e17f38f 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; @@ -66,6 +67,7 @@ impl DisallowedMethods { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, + PathNS::Value, |def_kind| { matches!( def_kind, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 2bae82648ac7..d0b2f0c8407f 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; @@ -60,7 +61,14 @@ pub struct DisallowedTypes { impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true); + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); Self { def_ids, prim_tys } } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index ab77edf1147c..87da380e9540 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -93,7 +93,7 @@ declare_clippy_lint! { /// ```no_run /// //! [first](x)second /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub DOC_LINK_CODE, nursery, "link with code back-to-back with other code" 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 553a00ed868d..e653a57196d6 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -759,12 +759,12 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { let recv_ty = cx.typeck_results().expr_ty(receiver); if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { - match path.ident.name.as_str() { - "ln" => check_ln1p(cx, expr, receiver), - "log" => check_log_base(cx, expr, receiver, args), - "powf" => check_powf(cx, expr, receiver, args), - "powi" => check_powi(cx, expr, receiver, args), - "sqrt" => check_hypot(cx, expr, receiver), + match path.ident.name { + sym::ln => check_ln1p(cx, expr, receiver), + sym::log => check_log_base(cx, expr, receiver, args), + sym::powf => check_powf(cx, expr, receiver, args), + sym::powi => check_powi(cx, expr, receiver, args), + sym::sqrt => check_hypot(cx, expr, receiver), _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 5f3fc5100e75..d0d02a382d15 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -9,8 +9,8 @@ mod too_many_arguments; mod too_many_lines; use clippy_config::Conf; -use clippy_utils::def_path_def_ids; use clippy_utils::msrvs::Msrv; +use clippy_utils::paths::{PathNS, lookup_path_str}; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; @@ -469,7 +469,7 @@ impl Functions { trait_ids: conf .allow_renamed_params_for .iter() - .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::>())) + .flat_map(|p| lookup_path_str(tcx, PathNS::Type, p)) .collect(), msrv: conf.msrv, } 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 514e72a48682..0823ef53ef98 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -69,7 +69,7 @@ declare_clippy_lint! { /// /// let result = a.saturating_sub(b); /// ``` - #[clippy::version = "1.44.0"] + #[clippy::version = "1.83.0"] pub INVERTED_SATURATING_SUB, correctness, "Check if a variable is smaller than another one and still subtract from it even if smaller" diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index b1271a264b54..3d4dcd020702 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -5,7 +5,7 @@ use clippy_utils::macros::span_is_local; use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; +use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Symbol; @@ -162,6 +162,7 @@ pub struct ItemNameRepetitions { enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, + allow_exact_repetitions: bool, allow_private_module_inception: bool, allowed_prefixes: FxHashSet, } @@ -173,6 +174,7 @@ impl ItemNameRepetitions { enum_threshold: conf.enum_variant_name_threshold, struct_threshold: conf.struct_field_name_threshold, avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + allow_exact_repetitions: conf.allow_exact_repetitions, allow_private_module_inception: conf.allow_private_module_inception, allowed_prefixes: conf.allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), } @@ -405,6 +407,7 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_> if count_match_start(item_name, name).char_count == item_name_chars && name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric()) + && !check_enum_tuple_path_match(name, variant.data) { span_lint_hir( cx, @@ -420,7 +423,9 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) let name = variant.ident.name.as_str(); let item_name_chars = item_name.chars().count(); - if count_match_end(item_name, name).char_count == item_name_chars { + if count_match_end(item_name, name).char_count == item_name_chars + && !check_enum_tuple_path_match(name, variant.data) + { span_lint_hir( cx, ENUM_VARIANT_NAMES, @@ -431,6 +436,27 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) } } +/// Checks if an enum tuple variant contains a single field +/// whose qualified path contains the variant's name. +fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_>) -> bool { + // Only check single-field tuple variants + let VariantData::Tuple(fields, ..) = variant_data else { + return false; + }; + if fields.len() != 1 { + return false; + } + // Check if field type is a path and contains the variant name + match fields[0].ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => path + .segments + .iter() + .any(|segment| segment.ident.name.as_str() == variant_name), + TyKind::Path(QPath::TypeRelative(_, segment)) => segment.ident.name.as_str() == variant_name, + _ => false, + } +} + impl LateLintPass<'_> for ItemNameRepetitions { fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { let Some(_ident) = item.kind.ident() else { return }; @@ -462,11 +488,21 @@ impl LateLintPass<'_> for ItemNameRepetitions { } // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() - && cx.tcx.visibility(mod_owner_id.def_id).is_public() - && item_camel.len() > mod_camel.len() - { + // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. + + let both_are_public = + cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); + + if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + + if both_are_public && item_camel.len() > mod_camel.len() { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index bdbf5b37c5f0..916191b2a7b0 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; +use clippy_utils::ty::{implements_trait, is_must_use_ty}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; use rustc_hir::{LetStmt, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -129,12 +129,6 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]); -const SYNC_GUARD_PATHS: [&[&str]; 3] = [ - &paths::PARKING_LOT_MUTEX_GUARD, - &paths::PARKING_LOT_RWLOCK_READ_GUARD, - &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, -]; - impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if matches!(local.source, LocalSource::Normal) @@ -144,7 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)), + GenericArgKind::Type(inner_ty) => inner_ty + .ty_adt_def() + .is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); if contains_sync_guard { diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 9c8488ff381b..1917ca24a05b 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_errors::Applicability; use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,13 +33,19 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { && !local.span.in_external_macro(cx.tcx.sess.source_map()) && !is_from_proc_macro(cx, ty) { - span_lint_and_help( + span_lint_and_then( cx, LET_WITH_TYPE_UNDERSCORE, local.span, "variable declared with type underscore", - Some(ty.span.with_lo(local.pat.span.hi())), - "remove the explicit type `_` declaration", + |diag| { + diag.span_suggestion_verbose( + ty.span.with_lo(local.pat.span.hi()), + "remove the explicit type `_` declaration", + "", + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index bc7fc60827a0..006145cc623c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -1,5 +1,4 @@ #![feature(array_windows)] -#![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(macro_metavar_expr_concat)] #![feature(f128)] @@ -7,7 +6,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(rustc_private)] @@ -96,6 +94,7 @@ mod cargo; mod casts; mod cfg_not_test; mod checked_conversions; +mod cloned_ref_to_slice_refs; mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; @@ -731,7 +730,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf))); + store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); @@ -748,7 +747,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); - store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); + store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))); store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))); store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); @@ -944,5 +943,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); + store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 5ef5e3a44f85..9a64226b1ed9 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -88,7 +88,7 @@ declare_clippy_lint! { /// x.chars() /// } /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.87.0"] pub ELIDABLE_LIFETIME_NAMES, pedantic, "lifetime name that can be replaced with the anonymous lifetime" diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index 343f7c5d2d12..15c656cc7bc7 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{HasSession, snippet_with_applicability}; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_slice_like}; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; use rustc_ast::ast::LitKind; @@ -58,6 +58,8 @@ pub(super) fn check<'tcx>( && let Res::Local(idx_hir) = idx_path.res && !is_local_used(cx, assignval, idx_hir) && msrv.meets(cx, msrvs::SLICE_FILL) + && let slice_ty = cx.typeck_results().expr_ty(slice).peel_refs() + && is_slice_like(cx, slice_ty) { sugg(cx, body, expr, slice.span, assignval.span); } diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 2b66827e82ee..56d2bef2305a 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -778,7 +778,7 @@ declare_clippy_lint! { /// let _ = s[idx..]; /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.88.0"] pub CHAR_INDICES_AS_BYTE_INDICES, correctness, "using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected" diff --git a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs index c515e41f242f..bac4b3d32f2a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs +++ b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// a.abs_diff(b) /// # ; /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.88.0"] pub MANUAL_ABS_DIFF, complexity, "using an if-else pattern instead of `abs_diff`" diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index d92069edb6d0..57c03fbb2ed2 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,6 +1,7 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -10,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{Ty, UintTy}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -47,9 +48,9 @@ enum MatchType<'a, 'b> { fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> { if let MethodCall(path, expr, _, _) = kind { - let is_lower = match path.ident.name.as_str() { - "to_ascii_lowercase" => true, - "to_ascii_uppercase" => false, + let is_lower = match path.ident.name { + sym::to_ascii_lowercase => true, + sym::to_ascii_uppercase => false, _ => return None, }; let ty_raw = cx.typeck_results().expr_ty(expr); diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index d6ac6e106b4b..0b3bec714c0e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -4,11 +4,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; +use clippy_utils::{ + MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, +}; use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; @@ -91,14 +94,15 @@ impl<'tcx> QuestionMark { let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + + let pat_arm = &arms[1 - idx]; // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. // However, if it arrives in second position, its pattern may cover some cases already covered // by the diverging one. - // TODO: accept the non-diverging arm as a second position if patterns are disjointed. - if idx == 0 { + if idx == 0 && !is_arms_disjointed(cx, diverging_arm, pat_arm) { return; } - let pat_arm = &arms[1 - idx]; + let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else { return; }; @@ -110,6 +114,63 @@ impl<'tcx> QuestionMark { } } +/// Checks if the patterns of the arms are disjointed. Currently, we only support patterns of simple +/// enum variants without nested patterns or bindings. +/// +/// TODO: Support more complex patterns. +fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> bool { + if arm1.guard.is_some() || arm2.guard.is_some() { + return false; + } + + if !is_enum_variant(cx, arm1.pat) || !is_enum_variant(cx, arm2.pat) { + return false; + } + + true +} + +/// Returns `true` if the given pattern is a variant of an enum. +pub fn is_enum_variant(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + struct Pat<'hir>(&'hir rustc_hir::Pat<'hir>); + + impl<'hir> MaybePath<'hir> for Pat<'hir> { + fn qpath_opt(&self) -> Option<&QPath<'hir>> { + match self.0.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::Expr(&PatExpr { + kind: PatExprKind::Path(ref qpath), + .. + }) => Some(qpath), + _ => None, + } + } + + fn hir_id(&self) -> HirId { + self.0.hir_id + } + } + + let res = path_res(cx, &Pat(pat)); + matches!( + res, + Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) + ) +} + fn emit_manual_let_else( cx: &LateContext<'_>, span: Span, diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index b365dbf088f5..b55c11f2d5b6 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; @@ -80,26 +80,26 @@ impl LateLintPass<'_> for ManualOptionAsSlice { check_map(cx, callee, span, self.msrv); } }, - ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() { - "unwrap_or" => { + ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name { + sym::unwrap_or => { if is_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, - "unwrap_or_else" => { + sym::unwrap_or_else => { if returns_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, _ => {}, }, - ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() { - "map_or" => { + ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name { + sym::map_or => { if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } }, - "map_or_else" => { + sym::map_or_else => { if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } @@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"]) + paths::SLICE_FROM_REF.matches_path(cx, expr) } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index b64ae0b24d81..3ac2c9fc2b36 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath}; @@ -16,7 +17,7 @@ use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind - && let PatKind::Binding(_, pat_id, _, _) = pat.kind + && let PatKind::Binding(BindingMode(ByRef::No, _), pat_id, _, _) = pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match // against it. diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index a54d835b538c..28efd2038b38 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { /// let mut an_option = Some(0); /// let taken = an_option.replace(1); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MEM_REPLACE_OPTION_WITH_SOME, style, "replacing an `Option` with `Some` instead of `replace()`" diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index d07870d4951e..292fa08b5984 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::sym; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -21,8 +22,8 @@ pub(super) fn check<'tcx>( ) { if let ExprKind::MethodCall(path_segment, ..) = recv.kind && matches!( - path_segment.ident.name.as_str(), - "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" + path_segment.ident.name, + sym::to_lowercase | sym::to_uppercase | sym::to_ascii_lowercase | sym::to_ascii_uppercase ) { return; diff --git a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs index bdc834bd47a5..ec4b9c7ae2ee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs +++ b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{expr_or_init, paths}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args if let [error_kind, error] = args && !expr.span.from_expansion() && !error_kind.span.from_expansion() - && clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW) - && clippy_utils::is_expr_path_def_path( - cx, - clippy_utils::expr_or_init(cx, error_kind), - &clippy_utils::paths::IO_ERRORKIND_OTHER, - ) && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind + && paths::IO_ERROR_NEW.matches_path(cx, path) + && paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind)) && msrv.meets(cx, msrvs::IO_ERROR_OTHER) { span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs index 0274e31b4c33..3fa83cd39d1d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use super::MANUAL_C_STR_LITERALS; @@ -71,15 +71,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args && cx.tcx.sess.edition() >= Edition2021 && msrv.meets(cx, msrvs::C_STR_LITERALS) { - match fn_name.as_str() { - name @ ("from_bytes_with_nul" | "from_bytes_with_nul_unchecked") + match fn_name { + sym::from_bytes_with_nul | sym::from_bytes_with_nul_unchecked if !arg.span.from_expansion() && let ExprKind::Lit(lit) = arg.kind && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node => { - check_from_bytes(cx, expr, arg, name); + check_from_bytes(cx, expr, arg, fn_name); }, - "from_ptr" => check_from_ptr(cx, expr, arg), + sym::from_ptr => check_from_ptr(cx, expr, arg), _ => {}, } } @@ -106,13 +106,13 @@ fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) { } } /// Checks `CStr::from_bytes_with_nul(b"foo\0")` -fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: &str) { +fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: Symbol) { let (span, applicability) = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(method, ..) = parent.kind && [sym::unwrap, sym::expect].contains(&method.ident.name) { (parent.span, Applicability::MachineApplicable) - } else if method == "from_bytes_with_nul_unchecked" { + } else if method == sym::from_bytes_with_nul_unchecked { // `*_unchecked` returns `&CStr` directly, nothing needs to be changed (expr.span, Applicability::MachineApplicable) } else { 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 18978a1d2bc8..e2df8ce1513c 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,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, path_def_id}; +use clippy_utils::{path_res, sym}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -79,16 +80,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { } let ty = cx.typeck_results().expr_ty(expr); - let ty_str = ty.to_string(); - // `std::T::MAX` `std::T::MIN` constants - if let Some(id) = path_def_id(cx, expr) { - if match_def_path(cx, id, &["core", &ty_str, "MAX"]) { - return Some(MinMax::Max); - } - - if match_def_path(cx, id, &["core", &ty_str, "MIN"]) { - return Some(MinMax::Min); + // `T::MAX` and `T::MIN` constants + if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind + && let Res::PrimTy(_) = path_res(cx, base) + { + match seg.ident.name { + sym::MAX => return Some(MinMax::Max), + sym::MIN => return Some(MinMax::Min), + _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 10f4637d08f6..e0e6a1a59b62 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4428,7 +4428,7 @@ declare_clippy_lint! { /// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap()); /// file.bytes(); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub UNBUFFERED_BYTES, perf, "calling .bytes() is very inefficient when data is not in memory" @@ -4453,7 +4453,7 @@ declare_clippy_lint! { /// values.contains(&10) /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MANUAL_CONTAINS, perf, "unnecessary `iter().any()` on slices that can be replaced with `contains()`" @@ -4709,6 +4709,8 @@ impl_lint_pass!(Methods => [ ]); /// Extracts a method call name, args, and `Span` of the method name. +/// This ensures that neither the receiver nor any of the arguments +/// come from expansion. pub fn method_call<'tcx>( recv: &'tcx Expr<'tcx>, ) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { @@ -4907,6 +4909,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { @@ -5049,29 +5052,12 @@ impl Methods { Some(("err", recv, [], err_span, _)) => { err_expect::check(cx, expr, recv, span, err_span, self.msrv); }, - _ => unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ), + _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("expect_err", [_]) => { + ("expect_err", [_]) | ("unwrap_err" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ); }, ("extend", [arg]) => { string_extend_chars::check(cx, expr, recv, arg); @@ -5437,27 +5423,6 @@ impl Methods { _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); - }, - ("unwrap_err", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); }, ("unwrap_or", [u_arg]) => { match method_call(recv) { @@ -5486,9 +5451,6 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_unchecked" | "unwrap_err_unchecked", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - }, ("unwrap_or_else", [u_arg]) => { match method_call(recv) { Some(("map", recv, [map_arg], _, _)) @@ -5526,6 +5488,56 @@ impl Methods { _ => {}, } } + // Handle method calls whose receiver and arguments may come from expansion + if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + match (path.ident.name.as_str(), args) { + ("expect", [_]) if !matches!(method_call(recv), Some(("ok" | "err", _, [], _, _))) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + ("expect_err", [_]) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + ("unwrap", []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + ("unwrap_err", []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + _ => {}, + } + } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index f528f7f065c6..71c1576cd57d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,9 +7,8 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{match_def_path, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -76,9 +75,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && let ExprKind::Path(path) = fn_path.kind - && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) + && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index cd22583b8a25..4c1ed6a1d833 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -357,20 +357,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let Some(hir_id) = self.current_statement_hir_id { self.hir_id_uses_map.insert(hir_id, self.uses.len()); } - match method_name.ident.name.as_str() { - "into_iter" => self.uses.push(Some(IterFunction { + match method_name.ident.name { + sym::into_iter => self.uses.push(Some(IterFunction { func: IterFunctionKind::IntoIter(expr.hir_id), span: expr.span, })), - "len" => self.uses.push(Some(IterFunction { + sym::len => self.uses.push(Some(IterFunction { func: IterFunctionKind::Len, span: expr.span, })), - "is_empty" => self.uses.push(Some(IterFunction { + sym::is_empty => self.uses.push(Some(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span, })), - "contains" => self.uses.push(Some(IterFunction { + sym::contains => self.uses.push(Some(IterFunction { func: IterFunctionKind::Contains(args[0].span), span: expr.span, })), diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index da084871402a..bce314e64f05 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::{match_any_def_paths, paths}; +use clippy_utils::paths; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -13,7 +13,7 @@ use rustc_span::{Span, sym}; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS) + is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { @@ -126,14 +126,14 @@ fn get_open_options( && let ExprKind::Path(path) = callee.kind && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() { - let std_file_options = [sym::file_options, sym::open_options_new]; + let is_std_options = matches!( + cx.tcx.get_diagnostic_name(did), + Some(sym::file_options | sym::open_options_new) + ); - let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS]; - - let is_std_options = std_file_options - .into_iter() - .any(|sym| cx.tcx.is_diagnostic_item(sym, did)); - is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some() + is_std_options + || paths::TOKIO_IO_OPEN_OPTIONS_NEW.matches(cx, did) + || paths::TOKIO_FILE_OPTIONS.matches(cx, did) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs index 91643b0dfefd..df8544f92203 100644 --- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; -use clippy_utils::{is_expr_final_block_expr, peel_blocks}; +use clippy_utils::{get_parent_expr, peel_blocks}; use super::RETURN_AND_THEN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'_>, ) { - if !is_expr_final_block_expr(cx.tcx, expr) { + if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() { return; } @@ -55,12 +55,24 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - let sugg = format!( - "let {} = {}?;\n{}", - arg_snip, - recv_snip, - reindent_multiline(inner, false, indent_of(cx, expr.span)) - ); + // If suggestion is going to get inserted as part of a `return` expression, it must be blockified. + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + let base_indent = indent_of(cx, parent_expr.span); + let inner_indent = base_indent.map(|i| i + 4); + format!( + "{}\n{}\n{}", + reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent), + reindent_multiline(inner, false, inner_indent), + reindent_multiline("}", false, base_indent), + ) + } else { + format!( + "let {} = {}?;\n{}", + arg_snip, + recv_snip, + reindent_multiline(inner, false, indent_of(cx, expr.span)) + ) + }; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index d183457da25a..c8efb600f576 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, path_to_local_id, paths}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -288,7 +288,7 @@ fn parse_iter_usage<'tcx>( match (name.ident.as_str(), args) { ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { - return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE) + return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did) && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind() && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) && let ty::Tuple(subs) = subs.type_at(0).kind() diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index 0cbf6004be3a..17e2620d9dd4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -1,9 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; -use clippy_utils::{ - get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs, -}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } - } else if match_def_path(cx, def_id, &["core", "option", "Option", call_name]) - || match_def_path(cx, def_id, &["core", "result", "Result", call_name]) + } else if let Some(impl_id) = cx.tcx.opt_parent(def_id) + && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() + && (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did())) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); 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 1f142bc3ba63..f3e24044fb6c 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 @@ -155,9 +155,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let mir = cx.tcx.mir_drops_elaborated_and_const_checked(def_id); + let mir = cx.tcx.optimized_mir(def_id); - if let Ok(()) = is_min_const_fn(cx, &mir.borrow(), self.msrv) + if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = cx.tcx.hir_node_by_def_id(def_id) { diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index b234b190153b..7772051eb5c6 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -48,6 +48,8 @@ pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. crate_items_only: bool, + /// Whether to allow fields starting with an underscore to skip documentation requirements + allow_unused: bool, /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. doc_hidden_stack: Vec, @@ -59,6 +61,7 @@ impl MissingDoc { pub fn new(conf: &'static Conf) -> Self { Self { crate_items_only: conf.missing_docs_in_crate_items, + allow_unused: conf.missing_docs_allow_unused, doc_hidden_stack: vec![false], prev_span: None, } @@ -260,11 +263,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { - if !sf.is_positional() { + if !(sf.is_positional() + || is_from_proc_macro(cx, sf) + || self.allow_unused && sf.ident.as_str().starts_with('_')) + { let attrs = cx.tcx.hir_attrs(sf.hir_id); - if !is_from_proc_macro(cx, sf) { - self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); - } + self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); } self.prev_span = Some(sf.span); } diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 66631a692063..a1e621cc9f6b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; -use clippy_utils::def_path_def_ids; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::{PathNS, lookup_path_str}; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -56,8 +56,12 @@ impl ImportRename { renames: conf .enforced_import_renames .iter() - .map(|x| (x.path.split("::").collect::>(), Symbol::intern(&x.rename))) - .flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename))) + .map(|x| (&x.path, Symbol::intern(&x.rename))) + .flat_map(|(path, rename)| { + lookup_path_str(tcx, PathNS::Arbitrary, path) + .into_iter() + .map(move |id| (id, rename)) + }) .collect(), } } 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 270eebe07580..3b271ca0dc14 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -42,10 +43,9 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); if !matches!( - macro_name.as_str(), - "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::debug_assert_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) { return; } @@ -60,7 +60,10 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - format!("do not call a function with mutable arguments inside of `{macro_name}!`"), + format!( + "do not call a function with mutable arguments inside of `{}!`", + cx.tcx.item_name(macro_call.def_id) + ), ); } } diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index f6bc9428d65f..f66b9519317b 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -1,13 +1,14 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{def_path_def_ids, fn_def_id, is_no_std_crate, path_def_id}; +use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind}; +use rustc_hir::{self as hir, BodyId, Expr, ExprKind, HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -62,10 +63,7 @@ static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ pub struct NonStdLazyStatic { msrv: Msrv, - lazy_static_lazy_static: Vec, - once_cell_crate: Vec, - once_cell_sync_lazy: Vec, - once_cell_sync_lazy_new: Vec, + once_cell_crates: Vec, sugg_map: FxIndexMap>, lazy_type_defs: FxIndexMap, uses_other_once_cell_types: bool, @@ -76,10 +74,7 @@ impl NonStdLazyStatic { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv, - lazy_static_lazy_static: Vec::new(), - once_cell_crate: Vec::new(), - once_cell_sync_lazy: Vec::new(), - once_cell_sync_lazy_new: Vec::new(), + once_cell_crates: Vec::new(), sugg_map: FxIndexMap::default(), lazy_type_defs: FxIndexMap::default(), uses_other_once_cell_types: false, @@ -95,17 +90,15 @@ fn can_use_lazy_cell(cx: &LateContext<'_>, msrv: Msrv) -> bool { impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_crate(&mut self, cx: &LateContext<'hir>) { - // Fetch def_ids for external paths - self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect(); - self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect(); - self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect(); - // And CrateNums for `once_cell` crate - self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect(); + // Add CrateNums for `once_cell` crate + self.once_cell_crates = find_crates(cx.tcx, sym::once_cell) + .iter() + .map(|def_id| def_id.krate) + .collect(); // Convert hardcoded fn replacement list into a map with def_id for (path, sugg) in FUNCTION_REPLACEMENTS { - let path_vec: Vec<&str> = path.split("::").collect(); - for did in def_path_def_ids(cx.tcx, &path_vec) { + for did in lookup_path_str(cx.tcx, PathNS::Value, path) { self.sugg_map.insert(did, sugg.map(ToOwned::to_owned)); } } @@ -114,7 +107,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) { if let ItemKind::Static(..) = item.kind && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span) - && self.lazy_static_lazy_static.contains(¯o_call.def_id) + && paths::LAZY_STATIC.matches(cx, macro_call.def_id) && can_use_lazy_cell(cx, self.msrv) { span_lint( @@ -130,7 +123,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { return; } - if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) + if let Some(lazy_info) = LazyInfo::from_item(cx, item) && can_use_lazy_cell(cx, self.msrv) { self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info); @@ -155,9 +148,9 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id() // Is from `once_cell` crate - && self.once_cell_crate.contains(&ty_def_id.krate) + && self.once_cell_crates.contains(&ty_def_id.krate) // And is NOT `once_cell::sync::Lazy` - && !self.once_cell_sync_lazy.contains(&ty_def_id) + && !paths::ONCE_CELL_SYNC_LAZY.matches(cx, ty_def_id) { self.uses_other_once_cell_types = true; } @@ -180,6 +173,8 @@ struct LazyInfo { /// // ^^^^ /// ``` ty_span_no_args: Span, + /// Item on which the lint must be generated. + item_hir_id: HirId, /// `Span` and `DefId` of calls on `Lazy` type. /// i.e.: /// ```ignore @@ -190,12 +185,12 @@ struct LazyInfo { } impl LazyInfo { - fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option { + fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option { // Check if item is a `once_cell:sync::Lazy` static. if let ItemKind::Static(_, ty, _, body_id) = item.kind && let Some(path_def_id) = path_def_id(cx, ty) && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && state.once_cell_sync_lazy.contains(&path_def_id) + && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { let ty_span_no_args = path_span_without_args(path); let body = cx.tcx.hir_body(body_id); @@ -204,7 +199,7 @@ impl LazyInfo { let mut new_fn_calls = FxIndexMap::default(); for_each_expr::<(), ()>(cx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) - && state.once_cell_sync_lazy_new.contains(&fn_did) + && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { new_fn_calls.insert(call_span, fn_did); } @@ -213,6 +208,7 @@ impl LazyInfo { Some(LazyInfo { ty_span_no_args, + item_hir_id: item.hir_id(), calls_span_and_id: new_fn_calls, }) } else { @@ -236,9 +232,10 @@ impl LazyInfo { } } - span_lint_and_then( + span_lint_hir_and_then( cx, NON_STD_LAZY_STATICS, + self.item_hir_id, self.ty_span_no_args, "this type has been superseded by `LazyLock` in the standard library", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs index 1421893274f5..d79101a687df 100644 --- a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs @@ -1,20 +1,18 @@ use clippy_utils::ast_utils::is_useless_with_eq_exprs; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; -use clippy_utils::{eq_expr_value, is_in_test_function}; +use clippy_utils::{eq_expr_value, is_in_test_function, sym}; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; use super::EQ_OP; pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { - let name = cx.tcx.item_name(macro_call.def_id); + if let Some(macro_call) = first_node_macro_backtrace(cx, e).find(|macro_call| { matches!( - name.as_str(), - "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) - .then(|| (macro_call, name)) }) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) && eq_expr_value(cx, lhs, rhs) && macro_call.is_local() @@ -24,7 +22,10 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - format!("identical args used in this `{macro_name}!` macro call"), + format!( + "identical args used in this `{}!` macro call", + cx.tcx.item_name(macro_call.def_id) + ), ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs index 76eba7327cff..7b98afa9b40b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs +++ b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::symbol::sym; use super::INTEGER_DIVISION; @@ -13,7 +15,8 @@ pub(crate) fn check<'tcx>( ) { if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() - && cx.typeck_results().expr_ty(right).is_integral() + && let right_ty = cx.typeck_results().expr_ty(right) + && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { 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 eebc62e2a5a9..ee1d59490ce9 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,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; @@ -69,10 +69,11 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir return ControlFlow::Continue(Descend::Yes); }; if !is_inside_always_const_context(cx.tcx, e.hir_id) - && matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "panic" | "assert" | "assert_eq" | "assert_ne" - ) + && (is_panic(cx, macro_call.def_id) + || matches!( + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) + )) { panics.push(macro_call.span); ControlFlow::Continue(Descend::No) diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 39ae9967e016..8962f36db1e6 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -113,8 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { ); return; } - match cx.tcx.item_name(macro_call.def_id).as_str() { - "todo" => { + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::todo_macro) => { span_lint( cx, TODO, @@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`todo` should not be present in production code", ); }, - "unimplemented" => { + Some(sym::unimplemented_macro) => { span_lint( cx, UNIMPLEMENTED, @@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`unimplemented` should not be present in production code", ); }, - "unreachable" => { + Some(sym::unreachable_macro) => { span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 834ff2af0e88..89d945161f62 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -2,8 +2,9 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths::PathLookup; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths, sym}; +use clippy_utils::{path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -121,17 +122,9 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS] impl<'tcx> LateLintPass<'tcx> for Regex { fn check_crate(&mut self, cx: &LateContext<'tcx>) { - // We don't use `match_def_path` here because that relies on matching the exact path, which changed - // between regex 1.8 and 1.9 - // - // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only - // perform the operation once and store the results - let regex_crates = find_crates(cx.tcx, sym::regex); - let mut resolve = |path: &[&str], kind: RegexKind| { - for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) { - if let Some(id) = res.opt_def_id() { - self.definitions.insert(id, kind); - } + let mut resolve = |path: &PathLookup, kind: RegexKind| { + for &id in path.get(cx) { + self.definitions.insert(id, kind); } }; diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index d8e8ead29128..122d97fdf819 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::for_each_expr; use clippy_utils::{ binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, + span_find_starting_semi, sym, }; use core::ops::ControlFlow; use rustc_ast::MetaItemInner; @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span, sym}; +use rustc_span::{BytePos, Pos, Span}; use std::borrow::Cow; use std::fmt::Display; @@ -411,8 +411,8 @@ fn check_final_expr<'tcx>( && let [tool, lint_name] = meta_item.path.segments.as_slice() && tool.ident.name == sym::clippy && matches!( - lint_name.ident.name.as_str(), - "needless_return" | "style" | "all" | "warnings" + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings ) { // This is an expectation of the `needless_return` lint diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index a8c6518b592b..a64b9b223786 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{get_trait_def_id, paths}; +use clippy_utils::paths; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,9 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) - && did == visit_did - { + if paths::SERDE_DE_VISITOR.matches(cx, did) { let mut seen_str = None; let mut seen_string = None; for item in *items { diff --git a/src/tools/clippy/clippy_lints/src/single_option_map.rs b/src/tools/clippy/clippy_lints/src/single_option_map.rs index 1fb54950612a..cc497c97a472 100644 --- a/src/tools/clippy/clippy_lints/src/single_option_map.rs +++ b/src/tools/clippy/clippy_lints/src/single_option_map.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// param * 2 /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub SINGLE_OPTION_MAP, nursery, "Checks for functions with method calls to `.map(_)` on an arg of type `Option` as the outermost expression." diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index 2d989b1cf0ba..54d09ff9ee40 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, is_no_std_crate}; +use clippy_utils::{is_no_std_crate, paths}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; @@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { && let Some(start_snippet) = start.span.get_source_text(cx) && let Some(end_snippet) = end.span.get_source_text(cx) { - let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) + let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx) && implements_trait(cx, ty, step_def_id, &[]) { true diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index af4d0d541f17..73a9fe71e001 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let ExprKind::MethodCall(path, recv, [], _) = &e.kind && path.ident.name == sym::into_bytes && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind - && matches!(path.ident.name.as_str(), "to_owned" | "to_string") + && matches!(path.ident.name, sym::to_owned | sym::to_string) && let ExprKind::Lit(lit) = &recv.kind && let LitKind::Str(lit_content, _) = &lit.node && lit_content.as_str().is_ascii() 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 bb969bc802fe..7d7d74f27b3c 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,11 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, sym}; +use clippy_utils::{is_in_const_context, paths, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -33,34 +34,35 @@ declare_clippy_lint! { "`char.is_digit()` is clearer" } -declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); +impl_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); + +pub(crate) struct ToDigitIsSome { + msrv: Msrv, +} + +impl ToDigitIsSome { + pub(crate) fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind && is_some_path.ident.name == sym::is_some { - let match_result = match &to_digit_expr.kind { + let match_result = match to_digit_expr.kind { hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { if to_digits_path.ident.name == sym::to_digit - && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) - && *char_arg_ty.kind() == ty::Char + && cx.typeck_results().expr_ty_adjusted(char_arg).is_char() { - Some((true, *char_arg, radix_arg)) + Some((true, char_arg, radix_arg)) } else { None } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind - && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) - && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path( - cx, - to_digits_def_id, - &["core", "char", "methods", "", "to_digit"], - ) - { + if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) { Some((false, char_arg, radix_arg)) } else { None @@ -69,7 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { _ => None, }; - if let Some((is_method_call, char_arg, radix_arg)) = match_result { + if let Some((is_method_call, char_arg, radix_arg)) = match_result + && (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_CHAR_IS_DIGIT)) + { let mut applicability = Applicability::MachineApplicable; let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 8aac3a591029..45e54302e32c 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -15,7 +15,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -282,18 +282,18 @@ impl TraitBounds { .iter() .copied() .chain(p.bounds.iter()) - .filter_map(get_trait_info_from_bound) - .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) + .map(|bound| snippet_with_applicability(cx, bound.span(), "_", &mut applicability)) .join(" + "); let hint_string = format!( "consider combining the bounds: `{}: {trait_bounds}`", snippet(cx, p.bounded_ty.span, "_"), ); + let ty_name = snippet(cx, p.bounded_ty.span, "_"); span_lint_and_help( cx, TYPE_REPETITION_IN_BOUNDS, bound.span, - "this type has already been used as a bound predicate", + format!("type `{ty_name}` has already been used as a bound predicate"), None, hint_string, ); @@ -395,15 +395,7 @@ impl Hash for ComparableTraitRef<'_, '_> { fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> { if let GenericBound::Trait(t) = bound { let trait_path = t.trait_ref.path; - let trait_span = { - let path_span = trait_path.span; - if let BoundPolarity::Maybe(_) = t.modifiers.polarity { - path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?` - } else { - path_span - } - }; - Some((trait_path.res, trait_path.segments, trait_span)) + Some((trait_path.res, trait_path.segments, t.span)) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs index 1ccab62708b1..535c044f49e6 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_normalizable; use clippy_utils::{eq_expr_value, path_to_local, sym}; use rustc_abi::WrappingRange; use rustc_errors::Applicability; @@ -84,8 +83,6 @@ pub(super) fn check<'tcx>( && path.ident.name == sym::then_some && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) - && is_normalizable(cx, cx.param_env, from_ty) - && is_normalizable(cx, cx.param_env, to_ty) // we only want to lint if the target type has a niche that is larger than the one of the source type // e.g. `u8` to `NonZero` should lint, but `NonZero` to `u8` should not && let Ok(from_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(from_ty)) diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index f2da8d35cb4f..d5112e2c3f97 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -1,13 +1,9 @@ mod crosspointer_transmute; mod eager_transmute; mod missing_transmute_annotations; -mod transmute_float_to_int; mod transmute_int_to_bool; -mod transmute_int_to_char; -mod transmute_int_to_float; mod transmute_int_to_non_zero; mod transmute_null_to_fn; -mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; mod transmute_ref_to_ref; @@ -141,40 +137,6 @@ declare_clippy_lint! { "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 - /// ```no_run - /// let x = 1_u32; - /// unsafe { - /// let _: char = std::mem::transmute(x); // where x: u32 - /// } - /// - /// // should be: - /// let _ = std::char::from_u32(x).unwrap(); - /// ``` - #[clippy::version = "pre 1.29.0"] - 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`. @@ -232,29 +194,6 @@ declare_clippy_lint! { "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. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: f32 = std::mem::transmute(1_u32); // where x: u32 - /// } - /// - /// // should be: - /// let _: f32 = f32::from_bits(1_u32); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TRANSMUTE_INT_TO_FLOAT, - complexity, - "transmutes from an integer to a float" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from `T` to `NonZero`, and suggests the `new_unchecked` @@ -280,52 +219,6 @@ declare_clippy_lint! { "transmutes from an integer to a non-zero wrapper" } -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. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: u32 = std::mem::transmute(1f32); - /// } - /// - /// // should be: - /// let _: u32 = 1f32.to_bits(); - /// ``` - #[clippy::version = "1.41.0"] - pub TRANSMUTE_FLOAT_TO_INT, - complexity, - "transmutes from a float to an integer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from a number to an array of `u8` - /// - /// ### Why this is bad? - /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes` - /// is intuitive and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let x: [u8; 8] = std::mem::transmute(1i64); - /// } - /// - /// // should be - /// let x: [u8; 8] = 0i64.to_ne_bytes(); - /// ``` - #[clippy::version = "1.58.0"] - pub TRANSMUTE_NUM_TO_BYTES, - complexity, - "transmutes from a number to an array of `u8`" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from a pointer to a pointer, or @@ -581,13 +474,9 @@ impl_lint_pass!(Transmute => [ 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_INT_TO_NON_ZERO, - TRANSMUTE_FLOAT_TO_INT, - TRANSMUTE_NUM_TO_BYTES, UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, @@ -632,14 +521,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) - | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) - | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) || transmute_undefined_repr::check(cx, e, from_ty, to_ty)) | (eager_transmute::check(cx, e, arg, from_ty, to_ty)); 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 deleted file mode 100644 index df2f681a1629..000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::TRANSMUTE_FLOAT_TO_INT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -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>, - mut arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) - if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => - { - span_lint_and_then( - cx, - TRANSMUTE_FLOAT_TO_INT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let mut sugg = sugg::Sugg::hir(cx, arg, ".."); - - if let ExprKind::Unary(UnOp::Neg, inner_expr) = &arg.kind { - arg = inner_expr; - } - - if let ExprKind::Lit(lit) = &arg.kind - // if the expression is a float literal and it is unsuffixed then - // add a suffix so the suggestion is valid and unambiguous - && let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node - { - let op = format!("{sugg}{}", float_ty.name_str()).into(); - match sugg { - sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), - _ => sugg = sugg::Sugg::NonParen(op), - } - } - - sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_paren()).into()); - - // cast the result of `to_bits` if `to_ty` is signed - sugg = if let ty::Int(int_ty) = to_ty.kind() { - sugg.as_ty(int_ty.name_str().to_string()) - } else { - sugg - }; - - diag.span_suggestion(e.span, "consider using", sugg, 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 deleted file mode 100644 index 81d10a7d5bde..000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::TRANSMUTE_INT_TO_CHAR; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{std_or_core, 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>, - arg: &'tcx Expr<'_>, - const_context: bool, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_CHAR, - e.span, - format!("transmute from a `{from_ty}` to a `char`"), - |diag| { - let Some(top_crate) = std_or_core(cx) else { return }; - let arg = sugg::Sugg::hir(cx, arg, ".."); - 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!("{top_crate}::char::from_u32({arg}).unwrap()"), - 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 deleted file mode 100644 index aaa95396b4b4..000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_INT_TO_FLOAT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -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>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_FLOAT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - let arg = if let ty::Int(int_ty) = from_ty.kind() { - 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!("{to_ty}::from_bits({arg})"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs deleted file mode 100644 index d72be270b731..000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_NUM_TO_BYTES; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty, UintTy}; - -/// 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>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => { - if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) { - return false; - } - if matches!(from_ty.kind(), ty::Float(_)) && const_context && !msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) - { - return false; - } - - span_lint_and_then( - cx, - TRANSMUTE_NUM_TO_BYTES, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - diag.span_suggestion( - e.span, - "consider using `to_ne_bytes()`", - format!("{arg}.to_ne_bytes()"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 3147058b4cda..c1c7cc516565 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -385,7 +385,7 @@ declare_clippy_lint! { /// ```no_run /// let right: std::borrow::Cow<'_, [u8]>; /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.87.0"] pub OWNED_COW, style, "needlessly owned Cow type" diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs index 6dcc1195a705..48b532968cb5 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -7,11 +8,12 @@ use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(macro_call) = root_macro_call_first_node(cx, expr) { - let macro_name = cx.tcx.item_name(macro_call.def_id); - let result = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + let result = match diag_name { + sym::assert_eq_macro | sym::debug_assert_eq_macro => "succeed", + sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail", _ => return, }; let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -24,7 +26,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - format!("`{macro_name}` of unit values detected. This will always {result}"), + format!( + "`{}` of unit values detected. This will always {result}", + cx.tcx.item_name(macro_call.def_id) + ), ); } return; diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index 1c1c841e9641..8ceaa3dc58ec 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource}; +use rustc_hir::{Body, Defaultness, Expr, ExprKind, FnDecl, HirId, Node, TraitItem, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; @@ -116,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, def_id: LocalDefId, ) { - if !span.from_expansion() && fn_kind.asyncness().is_async() && !is_def_id_trait_method(cx, def_id) { + if !span.from_expansion() + && fn_kind.asyncness().is_async() + && !is_def_id_trait_method(cx, def_id) + && !is_default_trait_impl(cx, def_id) + { let mut visitor = AsyncFnVisitor { cx, found_await: false, @@ -189,3 +193,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { } } } + +fn is_default_trait_impl(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + matches!( + cx.tcx.hir_node_by_def_id(def_id), + Node::TraitItem(TraitItem { + defaultness: Defaultness::Default { .. }, + .. + }) + ) +} 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 2d88c490b1ab..5e1cb9e54f57 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, paths, peel_blocks}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -93,14 +93,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { return; } - let async_paths: [&[&str]; 4] = [ + let async_paths = [ &paths::TOKIO_IO_ASYNCREADEXT, &paths::TOKIO_IO_ASYNCWRITEEXT, &paths::FUTURES_IO_ASYNCREADEXT, &paths::FUTURES_IO_ASYNCWRITEEXT, ]; - if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) { + if async_paths.into_iter().any(|path| path.matches(cx, trait_id)) { return; } } @@ -226,7 +226,7 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if is_panic(cx, macro_call.def_id) { return !cx.tcx.hir_is_inside_const_context(expr.hir_id); } - matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable") + cx.tcx.is_diagnostic_item(sym::unreachable_macro, macro_call.def_id) } fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { @@ -291,19 +291,28 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { }, }; - match ( - is_trait_method(cx, call, sym::IoRead), - is_trait_method(cx, call, sym::IoWrite), - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT), - match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) - || match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT), - ) { - (true, _, _, _) => Some(IoOp::SyncRead(vectorized)), - (_, true, _, _) => Some(IoOp::SyncWrite(vectorized)), - (_, _, true, _) => Some(IoOp::AsyncRead(vectorized)), - (_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)), - _ => None, + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id) + && let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id) + { + if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) { + match diag_name { + sym::IoRead => Some(IoOp::SyncRead(vectorized)), + sym::IoWrite => Some(IoOp::SyncWrite(vectorized)), + _ => None, + } + } else if paths::FUTURES_IO_ASYNCREADEXT.matches(cx, trait_def_id) + || paths::TOKIO_IO_ASYNCREADEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncRead(vectorized)) + } else if paths::TOKIO_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + || paths::FUTURES_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncWrite(vectorized)) + } else { + None + } + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index ba140788bb54..c641d4e55b94 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -4,15 +4,15 @@ use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -111,7 +111,7 @@ struct UnwrapInfo<'tcx> { /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, /// The check's name, like `is_ok` - check_name: &'tcx PathSegment<'tcx>, + check_name: Symbol, /// The branch where the check takes place, like `if x.is_ok() { .. }` branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). @@ -133,12 +133,12 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && ["is_some", "is_none"].contains(&method_name) + fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) } - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && ["is_ok", "is_err"].contains(&method_name) + fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) } if let ExprKind::Binary(op, left, right) = &expr.kind { @@ -155,14 +155,10 @@ fn collect_unwrap_info<'tcx>( } else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind && let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) - && let name = method_name.ident.as_str() + && let name = method_name.ident.name && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) { - let unwrappable = match name { - "is_some" | "is_ok" => true, - "is_err" | "is_none" => false, - _ => unreachable!(), - }; + let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { UnwrappableKind::Option @@ -174,7 +170,7 @@ fn collect_unwrap_info<'tcx>( local_id, if_expr, check: expr, - check_name: method_name, + check_name: name, branch, safe_to_unwrap, kind, @@ -184,12 +180,12 @@ fn collect_unwrap_info<'tcx>( Vec::new() } -/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated, -/// *except* for if `Option::as_mut` is called. +/// A HIR visitor delegate that checks if a local variable of type `Option` or `Result` is mutated, +/// *except* for if `.as_mut()` is called. /// The reason for why we allow that one specifically is that `.as_mut()` cannot change -/// the option to `None`, and that is important because this lint relies on the fact that +/// the variant, and that is important because this lint relies on the fact that /// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if -/// the option is changed to None between `is_some` and `unwrap`. +/// the option is changed to None between `is_some` and `unwrap`, ditto for `Result`. /// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) struct MutationVisitor<'tcx> { is_mutated: bool, @@ -198,13 +194,13 @@ struct MutationVisitor<'tcx> { } /// Checks if the parent of the expression pointed at by the given `HirId` is a call to -/// `Option::as_mut`. +/// `.as_mut()`. /// /// Used by the mutation visitor to specifically allow `.as_mut()` calls. /// In particular, the `HirId` that the visitor receives is the id of the local expression /// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent /// expression: that will be where the actual method call is. -fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { +fn is_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind { @@ -218,14 +214,16 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { if let ty::BorrowKind::Mutable = bk && is_potentially_local_place(self.local_id, &cat.place) - && !is_option_as_mut_use(self.tcx, diag_expr_id) + && !is_as_mut_use(self.tcx, diag_expr_id) { self.is_mutated = true; } } - fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) { - self.is_mutated = true; + fn mutate(&mut self, cat: &PlaceWithHirId<'tcx>, _: HirId) { + if is_potentially_local_place(self.local_id, &cat.place) { + self.is_mutated = true; + } } fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} @@ -276,12 +274,10 @@ enum AsRefKind { /// If it isn't, the expression itself is returned. fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option) { if let ExprKind::MethodCall(path, recv, [], _) = expr.kind { - if path.ident.name == sym::as_ref { - (recv, Some(AsRefKind::AsRef)) - } else if path.ident.name == sym::as_mut { - (recv, Some(AsRefKind::AsMut)) - } else { - (expr, None) + match path.ident.name { + sym::as_ref => (recv, Some(AsRefKind::AsRef)), + sym::as_mut => (recv, Some(AsRefKind::AsMut)), + _ => (expr, None), } } else { (expr, None) @@ -296,6 +292,10 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { return; } + // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. + if matches!(expr.kind, ExprKind::Closure(_)) { + return; + } if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) { walk_expr(self, cond); self.visit_branch(expr, cond, then, false); @@ -332,8 +332,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { expr.span, format!( "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", - method_name.ident.name, - unwrappable.check_name.ident.as_str(), + method_name.ident.name, unwrappable.check_name, ), |diag| { if is_entire_condition { diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index b7dcd2ffb0ee..812c4df4ddde 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,16 +1,18 @@ -use clippy_utils::{get_attr, higher}; +use clippy_utils::{MaybePath, get_attr, higher, path_def_id}; +use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; use rustc_hir::{ self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, - FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; -use std::fmt::{Display, Formatter, Write as _}; +use std::fmt::{Display, Formatter}; declare_lint_pass!( /// ### What it does @@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, } } +fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String { + cx.get_def_path(id) + .iter() + .map(Symbol::as_str) + .filter(|s| !s.starts_with('<')) + .join("_") + .to_uppercase() +} + struct Binding { name: String, value: T, @@ -257,11 +268,44 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath(&self, qpath: &Binding<&QPath<'_>>) { + fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Ok(path) = path_to_string(qpath.value) { - chain!(self, "match_qpath({qpath}, &[{}])", path); + } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + && !def_id.is_local() + { + bind!(self, def_id); + chain!( + self, + "let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()" + ); + if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) { + chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})"); + } else { + chain!( + self, + "paths::{}.matches(cx, {def_id}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, def_id.value) + ); + } + } + } + + fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { + if let Some(id) = path_def_id(self.cx, path.value) + && !id.is_local() + { + if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { + chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name()); + } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { + chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); + } else { + chain!( + self, + "paths::{}.matches_path(cx, {path}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, id) + ); + } } } @@ -270,7 +314,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ConstArgKind::Path(ref qpath) => { bind!(self, qpath); chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind"); - self.qpath(qpath); }, ConstArgKind::Anon(anon_const) => { bind!(self, anon_const); @@ -394,12 +437,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, let_expr); kind!("Let({let_expr})"); self.pat(field!(let_expr.pat)); - // Does what ExprKind::Cast does, only adds a clause for the type - // if it's a path - if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); - self.qpath(qpath); + if let Some(ty) = let_expr.value.ty { + bind!(self, ty); + chain!(self, "let Some({ty}) = {let_expr}.ty"); + self.maybe_path(ty); } self.expr(field!(let_expr.init)); }, @@ -451,11 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Cast(expr, cast_ty) => { bind!(self, expr, cast_ty); kind!("Cast({expr}, {cast_ty})"); - if let TyKind::Path(ref qpath) = cast_ty.value.kind { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); - self.qpath(qpath); - } + self.maybe_path(cast_ty); self.expr(expr); }, ExprKind::Type(expr, _ty) => { @@ -561,10 +598,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(object); self.expr(index); }, - ExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); + ExprKind::Path(_) => { + self.maybe_path(expr); }, ExprKind::AddrOf(kind, mutability, inner) => { bind!(self, inner); @@ -608,7 +643,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath); + self.qpath(qpath, expr); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -648,7 +683,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } - fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); @@ -657,15 +692,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatExprKind::Lit { lit, negated } => { bind!(self, lit); bind!(self, negated); - kind!("Lit{{ref {lit}, {negated} }}"); + kind!("Lit {{ ref {lit}, {negated} }}"); self.lit(lit); }, PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), - PatExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, + PatExprKind::Path(_) => self.maybe_path(pat), } } @@ -697,7 +728,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Struct(ref qpath, fields, ignore) => { bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -711,7 +742,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::TupleStruct(ref qpath, fields, skip_pos) => { bind!(self, qpath, fields); kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { @@ -743,13 +774,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Expr(lit_expr) => { bind!(self, lit_expr); kind!("Expr({lit_expr})"); - self.pat_expr(lit_expr); + self.pat_expr(lit_expr, pat); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.pat_expr(e)); - end.if_some(|e| self.pat_expr(e)); + start.if_some(|e| self.pat_expr(e, pat)); + end.if_some(|e| self.pat_expr(e, pat)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); @@ -797,32 +828,3 @@ fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); get_attr(cx.sess(), attrs, "author").count() > 0 } - -fn path_to_string(path: &QPath<'_>) -> Result { - fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> { - match *path { - QPath::Resolved(_, path) => { - for (i, segment) in path.segments.iter().enumerate() { - if i > 0 { - *s += ", "; - } - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - } - }, - QPath::TypeRelative(ty, segment) => match &ty.kind { - TyKind::Path(inner_path) => { - inner(s, inner_path)?; - *s += ", "; - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - }, - other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), - }, - QPath::LangItem(..) => return Err(()), - } - - Ok(()) - } - let mut s = String::new(); - inner(&mut s, path)?; - Ok(s) -} diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index f24c127c4521..a8758b65c919 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -498,9 +498,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { None => return, }, LitKind::Char => ( - match lit.symbol.as_str() { - "\"" => "\\\"", - "\\'" => "'", + match lit.symbol { + sym::DOUBLE_QUOTE => "\\\"", + sym::BACKSLASH_SINGLE_QUOTE => "'", _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) { Some(stripped) => stripped, None => return, 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 a97643e0eaca..24b1381ba458 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,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; @@ -54,8 +54,6 @@ impl LateLintPass<'_> for ZeroSizedMapValues { // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 && !ty.has_escaping_bound_vars() - // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. - && is_normalizable(cx, cx.param_env, ty) && let Ok(layout) = cx.layout_of(ty) && layout.is_zst() { diff --git a/src/tools/clippy/clippy_lints_internal/Cargo.toml b/src/tools/clippy/clippy_lints_internal/Cargo.toml index 2a0ceac27a32..a8293a1ad395 100644 --- a/src/tools/clippy/clippy_lints_internal/Cargo.toml +++ b/src/tools/clippy/clippy_lints_internal/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints_internal" version = "0.0.1" -edition = "2021" +edition = "2024" [dependencies] clippy_config = { path = "../clippy_config" } diff --git a/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs index d7967a0cc022..407deb45db0a 100644 --- a/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt}; +use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -10,6 +10,8 @@ use rustc_span::Span; use std::borrow::{Borrow, Cow}; +use crate::internal_paths; + declare_tool_lint! { /// ### What it does /// Lints `span_lint_and_then` function calls, where the @@ -80,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { } if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind - && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) + && internal_paths::SPAN_LINT_AND_THEN.matches_path(cx, func) && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind && let body = cx.tcx.hir_body(body) && let only_expr = peel_blocks_with_stmt(body.value) diff --git a/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs new file mode 100644 index 000000000000..a1dacd359a06 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::paths; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::{AttrStyle, DelimArgs}; +use rustc_hir::def::Res; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{ + AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; +use rustc_middle::ty::TyCtxt; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_tool_lint! { + /// ### What it does + /// Checks for structs or enums that derive `serde::Deserialize` and that + /// do not have a `#[serde(deny_unknown_fields)]` attribute. + /// + /// ### Why is this bad? + /// If the struct or enum is used in [`clippy_config::conf::Conf`] and a + /// user inserts an unknown field by mistake, the user's error will be + /// silently ignored. + /// + /// ### Example + /// ```rust + /// #[derive(serde::Deserialize)] + /// pub struct DisallowedPath { + /// path: String, + /// reason: Option, + /// replacement: Option, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// #[derive(serde::Deserialize)] + /// #[serde(deny_unknown_fields)] + /// pub struct DisallowedPath { + /// path: String, + /// reason: Option, + /// replacement: Option, + /// } + /// ``` + pub clippy::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + Allow, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + report_in_external_macro: true +} + +declare_lint_pass!(DeriveDeserializeAllowingUnknown => [DERIVE_DESERIALIZE_ALLOWING_UNKNOWN]); + +impl<'tcx> LateLintPass<'tcx> for DeriveDeserializeAllowingUnknown { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + // Is this an `impl` (of a certain form)? + let ItemKind::Impl(Impl { + of_trait: + Some(TraitRef { + path: + Path { + res: Res::Def(_, trait_def_id), + .. + }, + .. + }), + self_ty: + Ty { + kind: + TyKind::Path(QPath::Resolved( + None, + Path { + res: Res::Def(_, self_ty_def_id), + .. + }, + )), + .. + }, + .. + }) = item.kind + else { + return; + }; + + // Is it an `impl` of the trait `serde::Deserialize`? + if !paths::SERDE_DESERIALIZE.get(cx).contains(trait_def_id) { + return; + } + + // Is it derived? + if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) { + return; + } + + // Is `self_ty` local? + let Some(local_def_id) = self_ty_def_id.as_local() else { + return; + }; + + // Does `self_ty` have a variant with named fields? + if !has_variant_with_named_fields(cx.tcx, local_def_id) { + return; + } + + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + + // Does `self_ty` have `#[serde(deny_unknown_fields)]`? + if let Some(tokens) = find_serde_attr_item(cx.tcx, hir_id) + && tokens.iter().any(is_deny_unknown_fields_token) + { + return; + } + + span_lint( + cx, + DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + item.span, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + ); + } +} + +// Determines whether `def_id` corresponds to an ADT with at least one variant with named fields. A +// variant has named fields if its `ctor` field is `None`. +fn has_variant_with_named_fields(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let ty = tcx.type_of(def_id).skip_binder(); + + let rustc_middle::ty::Adt(adt_def, _) = ty.kind() else { + return false; + }; + + adt_def.variants().iter().any(|variant_def| variant_def.ctor.is_none()) +} + +fn find_serde_attr_item(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&TokenStream> { + tcx.hir_attrs(hir_id).iter().find_map(|attribute| { + if let Attribute::Unparsed(attr_item) = attribute + && let AttrItem { + path: AttrPath { segments, .. }, + args: AttrArgs::Delimited(DelimArgs { tokens, .. }), + style: AttrStyle::Outer, + .. + } = &**attr_item + && segments.len() == 1 + && segments[0].as_str() == "serde" + { + Some(tokens) + } else { + None + } + }) +} + +fn is_deny_unknown_fields_token(tt: &TokenTree) -> bool { + if let TokenTree::Token(token, _) = tt + && token + .ident() + .is_some_and(|(token, _)| token.as_str() == "deny_unknown_fields") + { + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs b/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs new file mode 100644 index 000000000000..dc1e30ab2bdd --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs @@ -0,0 +1,17 @@ +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{sym, type_path, value_path}; + +// Paths inside rustc +pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); +pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw); +pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint); +pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol); +pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str); +pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym); +pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext); + +// Paths in clippy itself +pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); +pub static MSRV_STACK: PathLookup = type_path!(clippy_utils::msrvs::MsrvStack); +pub static PATH_LOOKUP_NEW: PathLookup = value_path!(clippy_utils::paths::PathLookup::new); +pub static SPAN_LINT_AND_THEN: PathLookup = value_path!(clippy_utils::diagnostics::span_lint_and_then); diff --git a/src/tools/clippy/clippy_lints_internal/src/invalid_paths.rs b/src/tools/clippy/clippy_lints_internal/src/invalid_paths.rs deleted file mode 100644 index bee87efa3fcd..000000000000 --- a/src/tools/clippy/clippy_lints_internal/src/invalid_paths.rs +++ /dev/null @@ -1,108 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::def_path_res; -use clippy_utils::diagnostics::span_lint; -use rustc_hir as hir; -use rustc_hir::Item; -use rustc_hir::def::DefKind; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, FloatTy}; -use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; - -declare_tool_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub clippy::INVALID_PATHS, - Warn, - "invalid path", - report_in_external_macro: true -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if mod_name.as_str() == "paths" - && let hir::ItemKind::Const(.., body_id) = item.kind - && let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env( - cx.tcx, - ty::TypingEnv::post_analysis(cx.tcx, item.owner_id), - cx.tcx.typeck(item.owner_id), - ) - .eval_simple(cx.tcx.hir_body(body_id).value) - && let Some(path) = path - .iter() - .map(|x| { - if let Constant::Str(s) = x { - Some(s.as_str()) - } else { - None - } - }) - .collect::>>() - && !check_path(cx, &path[..]) - { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if !def_path_res(cx.tcx, path).is_empty() { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedType::Float(FloatTy::F32), - SimplifiedType::Float(FloatTy::F64), - SimplifiedType::Slice, - SimplifiedType::Str, - SimplifiedType::Bool, - SimplifiedType::Char, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) - .copied(); - for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(item_def_id); - if path_syms.starts_with(&lang_item_path) - && let [item] = &path_syms[lang_item_path.len()..] - { - if matches!( - cx.tcx.def_kind(item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - - false -} diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs index b02d378619ca..43cde86504f5 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lib.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(let_chains, rustc_private)] +#![feature(rustc_private)] #![allow( clippy::missing_docs_in_private_items, clippy::must_use_candidate, @@ -32,7 +32,8 @@ extern crate rustc_span; mod almost_standard_lint_formulation; mod collapsible_calls; -mod invalid_paths; +mod derive_deserialize_allowing_unknown; +mod internal_paths; mod lint_without_lint_pass; mod msrv_attr_impl; mod outer_expn_data_pass; @@ -46,7 +47,7 @@ use rustc_lint::{Lint, LintStore}; static LINTS: &[&Lint] = &[ almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, - invalid_paths::INVALID_PATHS, + derive_deserialize_allowing_unknown::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, lint_without_lint_pass::DEFAULT_LINT, lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, @@ -66,10 +67,10 @@ pub fn register_lints(store: &mut LintStore) { store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths)); + store.register_late_pass(|_| Box::new(derive_deserialize_allowing_unknown::DeriveDeserializeAllowingUnknown)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); diff --git a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs index 6a75defcce34..655d8fb8d1bf 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -1,6 +1,7 @@ +use crate::internal_paths; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::is_lint_allowed; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{is_lint_allowed, match_def_path, paths}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; @@ -209,10 +210,10 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { && let TyKind::Path(ref path) = inner.kind && let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); + internal_paths::LINT.matches(cx, def_id) + } else { + false } - - false } fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { diff --git a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs index dda054546e26..d48d8dc57b24 100644 --- a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs @@ -1,7 +1,6 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::match_type; -use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -31,7 +30,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .tcx .impl_trait_ref(item.owner_id) .map(EarlyBinder::instantiate_identity) - && match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS) + && internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id) && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind() && self_ty_def.is_struct() && self_ty_def.all_fields().any(|f| { @@ -40,7 +39,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .instantiate_identity() .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV_STACK)) + .any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty())) }) && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes") { diff --git a/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs index e94419647978..40951443a48a 100644 --- a/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs @@ -1,6 +1,6 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::match_type; -use clippy_utils::{is_lint_allowed, method_calls, paths}; +use clippy_utils::{is_lint_allowed, method_calls}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { && let (self_arg, args) = arg_lists[1] && args.is_empty() && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && match_type(cx, self_ty, &paths::SYNTAX_CONTEXT) + && internal_paths::SYNTAX_CONTEXT.matches_ty(cx, self_ty) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints_internal/src/symbols.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs index c64e5821916b..5aee545fb0f5 100644 --- a/src/tools/clippy/clippy_lints_internal/src/symbols.rs +++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs @@ -1,11 +1,10 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::match_type; -use clippy_utils::{def_path_def_ids, match_def_path, paths}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, Lit, Node, Pat, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_lint_defs::declare_tool_lint; use rustc_middle::mir::ConstValue; @@ -66,17 +65,50 @@ pub struct Symbols { impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); +impl Symbols { + fn lit_suggestion(&self, lit: &Lit) -> Option<(Span, String)> { + if let LitKind::Str(name, _) = lit.node { + let sugg = if let Some((prefix, name)) = self.symbol_map.get(&name.as_u32()) { + format!("{prefix}::{name}") + } else { + format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")) + }; + Some((lit.span, sugg)) + } else { + None + } + } + + fn expr_suggestion(&self, expr: &Expr<'_>) -> Option<(Span, String)> { + if let ExprKind::Lit(lit) = expr.kind { + self.lit_suggestion(lit) + } else { + None + } + } + + fn pat_suggestions(&self, pat: &Pat<'_>, suggestions: &mut Vec<(Span, String)>) { + pat.walk_always(|pat| { + if let PatKind::Expr(pat_expr) = pat.kind + && let PatExprKind::Lit { lit, .. } = pat_expr.kind + { + suggestions.extend(self.lit_suggestion(lit)); + } + }); + } +} + impl<'tcx> LateLintPass<'tcx> for Symbols { fn check_crate(&mut self, cx: &LateContext<'_>) { let modules = [ - ("kw", &paths::KW_MODULE[..]), - ("sym", &paths::SYM_MODULE), - ("sym", &paths::CLIPPY_SYM_MODULE), + ("kw", &internal_paths::KW_MODULE), + ("sym", &internal_paths::SYM_MODULE), + ("sym", &internal_paths::CLIPPY_SYM_MODULE), ]; for (prefix, module) in modules { - for def_id in def_path_def_ids(cx.tcx, module) { + for def_id in module.get(cx) { // When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will - // still lint but the suggestion will say to add it to `sym.rs` even if it's already there + // still lint but the suggestion may suggest the incorrect name for symbols such as `sym::CRLF` if def_id.is_local() { continue; } @@ -84,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Symbols { for item in cx.tcx.module_children(def_id) { if let Res::Def(DefKind::Const, item_def_id) = item.res && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() - && match_type(cx, ty, &paths::SYMBOL) + && internal_paths::SYMBOL.matches_ty(cx, ty) && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) && let Some(value) = value.to_u32().discard_err() { @@ -99,8 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for Symbols { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) - && let ExprKind::Lit(lit) = arg.kind - && let LitKind::Str(name, _) = lit.node + && let Some((_, sugg)) = self.expr_suggestion(arg) { span_lint_and_then( cx, @@ -108,48 +139,55 @@ impl<'tcx> LateLintPass<'tcx> for Symbols { expr.span, "interning a string literal", |diag| { - let (message, path) = suggestion(&mut self.symbol_map, name); - diag.span_suggestion_verbose(expr.span, message, path, Applicability::MaybeIncorrect); + diag.span_suggestion_verbose( + expr.span, + "use a preinterned symbol instead", + sugg, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbol to `clippy_utils/src/sym.rs` if needed"); }, ); } - if let ExprKind::Binary(_, lhs, rhs) = expr.kind { - check_binary(cx, lhs, rhs, &mut self.symbol_map); - check_binary(cx, rhs, lhs, &mut self.symbol_map); - } - } -} + if let Some(as_str) = as_str_span(cx, expr) + && let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) + { + let mut suggestions = Vec::new(); -fn check_binary( - cx: &LateContext<'_>, - lhs: &Expr<'_>, - rhs: &Expr<'_>, - symbols: &mut FxHashMap, -) { - if let Some(removal_span) = as_str_span(cx, lhs) - && let ExprKind::Lit(lit) = rhs.kind - && let LitKind::Str(name, _) = lit.node - { - span_lint_and_then(cx, SYMBOL_AS_STR, lhs.span, "converting a Symbol to a string", |diag| { - let (message, path) = suggestion(symbols, name); - diag.multipart_suggestion_verbose( - message, - vec![(removal_span, String::new()), (rhs.span, path)], - Applicability::MachineApplicable, + match parent.kind { + ExprKind::Binary(_, lhs, rhs) => { + suggestions.extend(self.expr_suggestion(lhs)); + suggestions.extend(self.expr_suggestion(rhs)); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.pat_suggestions(arm.pat, &mut suggestions); + } + }, + _ => {}, + } + + if suggestions.is_empty() { + return; + } + + span_lint_and_then( + cx, + SYMBOL_AS_STR, + expr.span, + "converting a Symbol to a string", + |diag| { + suggestions.push((as_str, String::new())); + diag.multipart_suggestion( + "use preinterned symbols instead", + suggestions, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbols to `clippy_utils/src/sym.rs` if needed"); + }, ); - }); - } -} - -fn suggestion(symbols: &mut FxHashMap, name: Symbol) -> (&'static str, String) { - if let Some((prefix, name)) = symbols.get(&name.as_u32()) { - ("use the preinterned symbol", format!("{prefix}::{name}")) - } else { - ( - "add the symbol to `clippy_utils/src/sym.rs` and use it", - format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")), - ) + } } } @@ -160,7 +198,7 @@ fn suggestion(symbols: &mut FxHashMap, name: Symbol fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::MethodCall(_, recv, [], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && match_def_path(cx, method_def_id, &paths::SYMBOL_AS_STR) + && internal_paths::SYMBOL_AS_STR.matches(cx, method_def_id) { Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi())) } else { diff --git a/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs index 6bdfbed55b06..8877f1faf0ee 100644 --- a/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,23 +1,14 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; -use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; +use crate::internal_paths; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::{PathNS, lookup_path}; +use clippy_utils::{path_def_id, peel_ref_operators}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; +use rustc_lint_defs::{declare_lint_pass, declare_tool_lint}; use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; -use rustc_middle::ty::{self, Ty}; -use rustc_session::impl_lint_pass; -use rustc_span::Span; use rustc_span::symbol::Symbol; -use std::str; - declare_tool_lint! { /// ### What it does /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used. @@ -28,12 +19,14 @@ declare_tool_lint! { /// /// ### Example /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) + /// pub static VEC: PathLookup = path!(alloc::vec::Vec); + /// + /// VEC.contains_ty(cx, ty) /// ``` /// /// Use instead: /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) + /// is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` pub clippy::UNNECESSARY_DEF_PATH, Warn, @@ -41,257 +34,65 @@ declare_tool_lint! { report_in_external_macro: true } -impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[derive(Default)] -pub struct UnnecessaryDefPath { - array_def_ids: FxIndexSet<(DefId, Span)>, - linted_def_ids: FxHashSet, -} +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - match expr.kind { - ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), - ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), - _ => {}, - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for &(def_id, span) in &self.array_def_ids { - if self.linted_def_ids.contains(&def_id) { - continue; - } - - let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { - ("diagnostic item", format!("sym::{sym}")) - } else if let Some(sym) = get_lang_item_name(cx, def_id) { - ("language item", format!("LangItem::{sym}")) - } else { - continue; - }; - - span_lint_and_help( - cx, - UNNECESSARY_DEF_PATH, - span, - format!("hardcoded path to a {msg}"), - None, - format!("convert all references to use `{sugg}`"), - ); - } - } -} - -impl UnnecessaryDefPath { - #[allow(clippy::too_many_lines)] - fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { - enum Item { - LangItem(&'static str), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if let [cx_arg, def_arg, args @ ..] = args - && let ExprKind::Path(path) = &func.kind - && let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && let Some(which_path) = match_any_def_paths(cx, id, PATHS) - && let item_arg = if which_path == 4 { &args[1] } else { &args[0] } - // Extract the path to the matched type - && let Some(segments) = path_to_matched_type(cx, item_arg) - && let segments = segments.iter().map(|sym| &**sym).collect::>() - && let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next() + if let ExprKind::Call(ctor, [_, path]) = expr.kind + && internal_paths::PATH_LOOKUP_NEW.matches_path(cx, ctor) + && let ExprKind::Array(segments) = peel_ref_operators(cx, path).kind + && let Some(macro_id) = expr.span.ctxt().outer_expn_data().macro_def_id { - // Check if the target item is a diagnostic item or LangItem. - #[rustfmt::skip] - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(item_name) = get_lang_item_name(cx, def_id) { - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; + let ns = match cx.tcx.item_name(macro_id).as_str() { + "type_path" => PathNS::Type, + "value_path" => PathNS::Value, + "macro_path" => PathNS::Macro, + _ => unreachable!(), }; - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - _ => false, - }; + let path: Vec = segments + .iter() + .map(|segment| { + if let Some(const_def_id) = path_def_id(cx, segment) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) + && let Some(value) = value.to_u32().discard_err() + { + Symbol::new(value) + } else { + panic!("failed to resolve path {:?}", expr.span); + } + }) + .collect(); - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => ( - format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), - has_ctor, - ), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), - has_ctor, - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => { - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) - }, - // match_type - (2, Item::DiagnosticItem(item)) => ( - format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (2, Item::LangItem(item)) => ( - format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), - false, - ), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), - false, - ), - (3, Item::DiagnosticItem(item)) => ( - format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { - diag.span_suggestion(span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead", + for def_id in lookup_path(cx.tcx, ns, &path) { + if let Some(name) = cx.tcx.get_diagnostic_name(def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a diagnostic name exists for this path: sym::{name}"), + |diag| { + diag.help( + "remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead", + ); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils"); + }, + ); + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a language item exists for this path: LangItem::{item_name}"), + |diag| { + diag.help("remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead"); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils"); + }, ); } - }); - - self.linted_def_ids.insert(def_id); - } - } - - fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { - let Some(path) = path_from_array(elements) else { return }; - - for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::>()) { - self.array_def_ids.insert((def_id, span)); - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - if let Node::LetStmt(LetStmt { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id).instantiate_identity(), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => { - let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory(); - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => path_from_array(exprs), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().ptrs().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .ptrs() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok() - .map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -fn path_from_array(exprs: &[Expr<'_>]) -> Option> { - exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind - && let LitKind::Str(sym, _) = lit.node - { - return Some((*sym.as_str()).to_owned()); } - - None - }) - .collect() + } + } } fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index b98e99017503..ac970e1c4b0a 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 66192f866fa0..d4080d06d3ca 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-05-01 +nightly-2025-05-14 ``` diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 187dfa4dda84..0a9c39c41bd8 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,9 +1,6 @@ -#![feature(array_chunks)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(macro_metavar_expr_concat)] #![feature(macro_metavar_expr)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustc_private)] #![feature(assert_matches)] @@ -97,28 +94,28 @@ use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ - self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, - GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, - Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, - Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, + self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, + CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, + Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + TraitItemKind, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, - TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, + TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -131,7 +128,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; -use rustc_middle::hir::nested_filter; #[macro_export] macro_rules! extract_msrv_attr { @@ -239,7 +235,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool { /// * const blocks (or inline consts) /// * associated constants pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { - use ConstContext::{Const, ConstFn, Static}; + use rustc_hir::ConstContext::{Const, ConstFn, Static}; let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else { return false; }; @@ -347,14 +343,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the method call given in `expr` belongs to the given trait. -/// This is a deprecated function, consider using [`is_trait_method`]. -pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { - let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - let trt_id = cx.tcx.trait_of_item(def_id); - trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path)) -} - /// Checks if the given method call expression calls an inherent method. pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { @@ -438,44 +426,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator, segments: &[&str]) -> bool { - match *path { - QPath::Resolved(_, path) => match_path(path, segments), - QPath::TypeRelative(ty, segment) => match ty.kind { - TyKind::Path(ref inner_path) => { - if let [prefix @ .., end] = segments - && match_qpath(inner_path, prefix) - { - return segment.ident.name.as_str() == *end; - } - false - }, - _ => false, - }, - QPath::LangItem(..) => false, - } -} - -/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. -/// -/// Please use `is_path_diagnostic_item` if the target is a diagnostic item. -pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { - path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments)) -} - /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given lang item. pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { @@ -492,34 +442,6 @@ pub fn is_path_diagnostic_item<'tcx>( path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) } -/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals. -/// -/// 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()`. -/// -/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a -/// `rustc_hir::Path`. -/// -/// # Examples -/// -/// ```rust,ignore -/// if match_path(&trait_ref.path, &paths::HASH) { -/// // This is the `std::hash::Hash` trait. -/// } -/// -/// if match_path(ty_path, &["rustc", "lint", "Lint"]) { -/// // This is a `rustc_middle::lint::Lint`. -/// } -/// ``` -pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { - path.segments - .iter() - .rev() - .zip(segments.iter().rev()) - .all(|(a, b)| a.ident.name.as_str() == *b) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind @@ -586,201 +508,6 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { - let ty = match name { - "bool" => SimplifiedType::Bool, - "char" => SimplifiedType::Char, - "str" => SimplifiedType::Str, - "array" => SimplifiedType::Array, - "slice" => SimplifiedType::Slice, - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - "const_ptr" => SimplifiedType::Ptr(Mutability::Not), - "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), - "isize" => SimplifiedType::Int(IntTy::Isize), - "i8" => SimplifiedType::Int(IntTy::I8), - "i16" => SimplifiedType::Int(IntTy::I16), - "i32" => SimplifiedType::Int(IntTy::I32), - "i64" => SimplifiedType::Int(IntTy::I64), - "i128" => SimplifiedType::Int(IntTy::I128), - "usize" => SimplifiedType::Uint(UintTy::Usize), - "u8" => SimplifiedType::Uint(UintTy::U8), - "u16" => SimplifiedType::Uint(UintTy::U16), - "u32" => SimplifiedType::Uint(UintTy::U32), - "u64" => SimplifiedType::Uint(UintTy::U64), - "u128" => SimplifiedType::Uint(UintTy::U128), - "f32" => SimplifiedType::Float(FloatTy::F32), - "f64" => SimplifiedType::Float(FloatTy::F64), - _ => { - return [].iter().copied(); - }, - }; - - tcx.incoherent_impls(ty).iter().copied() -} - -fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .filter(|item| item.ident.name == name) - .map(|child| child.res.expect_non_local()) - .collect(), - DefKind::Impl { .. } => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) - .collect(), - _ => Vec::new(), - } -} - -fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec { - let root_mod; - let item_kind = match tcx.hir_node_by_def_id(local_id) { - Node::Crate(r#mod) => { - root_mod = ItemKind::Mod(Ident::dummy(), r#mod); - &root_mod - }, - Node::Item(item) => &item.kind, - _ => return Vec::new(), - }; - - let res = |ident: Ident, owner_id: OwnerId| { - if ident.name == name { - let def_id = owner_id.to_def_id(); - Some(Res::Def(tcx.def_kind(def_id), def_id)) - } else { - None - } - }; - - match item_kind { - ItemKind::Mod(_, r#mod) => r#mod - .item_ids - .iter() - .filter_map(|&item_id| { - let ident = tcx.hir_item(item_id).kind.ident()?; - res(ident, item_id.owner_id) - }) - .collect(), - ItemKind::Impl(r#impl) => r#impl - .items - .iter() - .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - ItemKind::Trait(.., trait_item_refs) => trait_item_refs - .iter() - .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - _ => Vec::new(), - } -} - -fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { - if let Some(local_id) = def_id.as_local() { - local_item_children_by_name(tcx, local_id, name) - } else { - non_local_item_children_by_name(tcx, def_id, name) - } -} - -/// Finds the crates called `name`, may be multiple due to multiple major versions. -pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .map(CrateNum::as_def_id) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .collect() -} - -/// Resolves a def path like `std::vec::Vec`. -/// -/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. -/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. -/// -/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` -/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. -/// -/// This function is expensive and should be used sparingly. -pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { - let (base, path) = match path { - [primitive] => { - return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; - }, - [base, path @ ..] => (base, path), - _ => return Vec::new(), - }; - - let base_sym = Symbol::intern(base); - - let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym { - Some(LOCAL_CRATE.as_def_id()) - } else { - None - }; - - let crates = find_primitive_impls(tcx, base) - .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .chain(find_crates(tcx, base_sym)) - .collect(); - - def_path_res_with_base(tcx, crates, path) -} - -/// Resolves a def path like `vec::Vec` with the base `std`. -/// -/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up -/// items from the same crate repeatedly, although should still be used sparingly. -pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec, mut path: &[&str]) -> Vec { - while let [segment, rest @ ..] = path { - path = rest; - let segment = Symbol::intern(segment); - - base = base - .into_iter() - .filter_map(|res| res.opt_def_id()) - .flat_map(|def_id| { - // When the current def_id is e.g. `struct S`, check the impl items in - // `impl S { ... }` - let inherent_impl_children = tcx - .inherent_impls(def_id) - .iter() - .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); - - let direct_children = item_children_by_name(tcx, def_id, segment); - - inherent_impl_children.chain(direct_children) - }) - .collect(); - } - - base -} - -/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. -pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator + use<> { - def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id()) -} - -/// Convenience function to get the `DefId` of a trait by path. -/// It could be a trait or trait alias. -/// -/// This function is expensive and should be used sparingly. -pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option { - def_path_res(tcx, path).into_iter().find_map(|res| match res { - Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), - _ => None, - }) -} - /// 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: @@ -2065,24 +1792,6 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { }) } -/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if -/// any. -/// -/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items. -pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option { - let search_path = cx.get_def_path(did); - paths - .iter() - .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied())) -} - -/// Checks if the given `DefId` matches the path. -pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool { - // We should probably move to Symbols in Clippy as well rather than interning every time. - let path = cx.get_def_path(did); - syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied()) -} - /// Checks if the given `DefId` matches the `libc` item. pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { let path = cx.get_def_path(did); diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 19061b574ff8..223a8649eb3c 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -22,7 +22,8 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } + 1,88,0 { LET_CHAINS } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT } 1,85,0 { UINT_FLOAT_MIDPOINT } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } @@ -38,7 +39,7 @@ msrv_aliases! { 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } - 1,63,0 { CLONE_INTO } + 1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN } 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } @@ -68,7 +69,7 @@ msrv_aliases! { 1,31,0 { OPTION_REPLACE } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } - 1,28,0 { FROM_BOOL, REPEAT_WITH } + 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } 1,24,0 { IS_ASCII_DIGIT } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 7f64ebd3b643..e5179e479ccd 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -4,62 +4,343 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -// Paths inside rustc -pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; -pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ - ["rustc_lint_defs", "Applicability", "Unspecified"], - ["rustc_lint_defs", "Applicability", "HasPlaceholders"], - ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], - ["rustc_lint_defs", "Applicability", "MachineApplicable"], -]; -pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"]; -pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; -pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; -pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; -pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; -pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; -pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; -pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; -pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; -pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; -pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; +use crate::{MaybePath, path_def_id, sym}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; +use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef, UseKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; +use std::sync::OnceLock; + +/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an +/// arbitrary namespace +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum PathNS { + Type, + Value, + Macro, + + /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return + /// either the macro or the module but **not** both + /// + /// Must only be used when the specific resolution is unimportant such as in + /// `missing_enforced_import_renames` + Arbitrary, +} + +impl PathNS { + fn matches(self, ns: Option) -> bool { + let required = match self { + PathNS::Type => TypeNS, + PathNS::Value => ValueNS, + PathNS::Macro => MacroNS, + PathNS::Arbitrary => return true, + }; + + ns == Some(required) + } +} + +/// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`]. +/// +/// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub struct PathLookup { + ns: PathNS, + path: &'static [Symbol], + once: OnceLock>, +} + +impl PathLookup { + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self { + Self { + ns, + path, + once: OnceLock::new(), + } + } + + /// Returns the list of [`DefId`]s that the path resolves to + pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + } + + /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into + /// stdlib crates to avoid the issue of multiple [`DefId`]s being returned + /// + /// May return [`None`] in `no_std`/`no_core` environments + pub fn only(&self, cx: &LateContext<'_>) -> Option { + let ids = self.get(cx); + debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0])); + debug_assert!(ids.len() <= 1, "{ids:?}"); + ids.first().copied() + } + + /// Checks if the path resolves to the given `def_id` + pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { + self.get(cx).contains(&def_id) + } + + /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool { + path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) + } + + /// Checks if the path resolves to `ty`'s definition, must be an `Adt` + pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + } +} + +macro_rules! path_macros { + ($($name:ident: $ns:expr,)*) => { + $( + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + #[macro_export] + macro_rules! $name { + ($$($$seg:ident $$(::)?)*) => { + PathLookup::new($ns, &[$$(sym::$$seg,)*]) + }; + } + )* + }; +} + +path_macros! { + type_path: PathNS::Type, + value_path: PathNS::Value, + macro_path: PathNS::Macro, +} // Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. -pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; -pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"]; -pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"]; -pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"]; - -// Paths in clippy itself -pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"]; -pub const CLIPPY_SYM_MODULE: [&str; 2] = ["clippy_utils", "sym"]; +pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of); +pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit); +pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new); +pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other); +pub static ITER_STEP: PathLookup = type_path!(core::iter::Step); +pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref); // Paths in external crates -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; -pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; -pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; -pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"]; -pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"]; -pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"]; -pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"]; -pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"]; -pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"]; -pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"]; -pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; -pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; -pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"]; +pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); +pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); +pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple); +pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [ + type_path!(lock_api::mutex::MutexGuard), + type_path!(lock_api::rwlock::RwLockReadGuard), + type_path!(lock_api::rwlock::RwLockWriteGuard), +]; +pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new); +pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new); +pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new); +pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new); +pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new); +pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new); +pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize); +pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor); +pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options); +pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt); +pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt); +pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions); +pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new); +pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static); +pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy); +pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new); + +// Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs` + +/// Equivalent to a [`lookup_path`] after splitting the input string on `::` +/// +/// This function is expensive and should be used sparingly. +pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec { + let path: Vec = path.split("::").map(Symbol::intern).collect(); + lookup_path(tcx, ns, &path) +} + +/// Resolves a def path like `std::vec::Vec`. +/// +/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// This function is expensive and should be used sparingly. +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec { + let (root, rest) = match *path { + [] | [_] => return Vec::new(), + [root, ref rest @ ..] => (root, rest), + }; + + let mut out = Vec::new(); + for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { + lookup_with_base(tcx, base, ns, rest, &mut out); + } + out +} + +/// Finds the crates called `name`, may be multiple due to multiple major versions. +pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { + static BY_NAME: OnceLock>> = OnceLock::new(); + let map = BY_NAME.get_or_init(|| { + let mut map = FxHashMap::default(); + map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); + for &num in tcx.crates(()) { + map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); + } + map + }); + match map.get(&name) { + Some(def_ids) => def_ids, + None => &[], + } +} + +fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { + let ty = match name { + sym::bool => SimplifiedType::Bool, + sym::char => SimplifiedType::Char, + sym::str => SimplifiedType::Str, + sym::array => SimplifiedType::Array, + sym::slice => SimplifiedType::Slice, + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), + sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), + sym::isize => SimplifiedType::Int(IntTy::Isize), + sym::i8 => SimplifiedType::Int(IntTy::I8), + sym::i16 => SimplifiedType::Int(IntTy::I16), + sym::i32 => SimplifiedType::Int(IntTy::I32), + sym::i64 => SimplifiedType::Int(IntTy::I64), + sym::i128 => SimplifiedType::Int(IntTy::I128), + sym::usize => SimplifiedType::Uint(UintTy::Usize), + sym::u8 => SimplifiedType::Uint(UintTy::U8), + sym::u16 => SimplifiedType::Uint(UintTy::U16), + sym::u32 => SimplifiedType::Uint(UintTy::U32), + sym::u64 => SimplifiedType::Uint(UintTy::U64), + sym::u128 => SimplifiedType::Uint(UintTy::U128), + sym::f32 => SimplifiedType::Float(FloatTy::F32), + sym::f64 => SimplifiedType::Float(FloatTy::F64), + _ => return &[], + }; + + tcx.incoherent_impls(ty) +} + +/// Resolves a def path like `vec::Vec` with the base `std`. +fn lookup_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec) { + loop { + match *path { + [segment] => { + out.extend(item_child_by_name(tcx, base, ns, segment)); + + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + let inherent_impl_children = tcx + .inherent_impls(base) + .iter() + .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); + out.extend(inherent_impl_children); + + return; + }, + [segment, ref rest @ ..] => { + path = rest; + let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { + return; + }; + base = child; + }, + [] => unreachable!(), + } + } +} + +fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + if let Some(local_id) = def_id.as_local() { + local_item_child_by_name(tcx, local_id, ns, name) + } else { + non_local_item_child_by_name(tcx, def_id, ns, name) + } +} + +fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option { + let root_mod; + let item_kind = match tcx.hir_node_by_def_id(local_id) { + Node::Crate(r#mod) => { + root_mod = ItemKind::Mod(Ident::dummy(), r#mod); + &root_mod + }, + Node::Item(item) => &item.kind, + _ => return None, + }; + + let res = |ident: Ident, owner_id: OwnerId| { + if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { + Some(owner_id.to_def_id()) + } else { + None + } + }; + + match item_kind { + ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { + let item = tcx.hir_item(item_id); + if let ItemKind::Use(path, UseKind::Single(ident)) = item.kind { + if ident.name == name { + path.res + .iter() + .find(|res| ns.matches(res.ns())) + .and_then(Res::opt_def_id) + } else { + None + } + } else { + res(item.kind.ident()?, item_id.owner_id) + } + }), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), + _ => None, + } +} + +fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { + if child.ident.name == name && ns.matches(child.res.ns()) { + child.res.opt_def_id() + } else { + None + } + }), + DefKind::Impl { .. } => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 38f077134c03..9428262b99aa 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -1,6 +1,6 @@ #![allow(non_upper_case_globals)] -use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol}; +use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT; #[doc(no_inline)] pub use rustc_span::sym::*; @@ -24,90 +24,177 @@ macro_rules! generate { ]; $( - pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); + pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); )* }; } generate! { abs, + align_of, + ambiguous_glob_reexports, as_bytes, as_deref_mut, as_deref, as_mut, + AsyncReadExt, + AsyncWriteExt, + BACKSLASH_SINGLE_QUOTE: r"\'", Binary, build_hasher, + bytes, cargo_clippy: "cargo-clippy", Cargo_toml: "Cargo.toml", cast, chars, CLIPPY_ARGS, CLIPPY_CONF_DIR, + clippy_utils, clone_into, cloned, collect, + const_ptr, contains, copied, CRLF: "\r\n", Current, + de, + Deserialize, + diagnostics, + disallowed_types, + DOUBLE_QUOTE: "\"", + EarlyLintPass, ends_with, + enum_glob_use, + error, + ErrorKind, exp, extend, finish_non_exhaustive, finish, flat_map, for_each, + from_bytes_with_nul_unchecked, + from_bytes_with_nul, + from_ptr, from_raw, + from_ref, from_str_radix, + fs, + futures_util, get, + hidden_glob_reexports, + hygiene, insert, int_roundings, into_bytes, into_owned, IntoIter, + io, is_ascii, is_empty, is_err, is_none, is_ok, is_some, + itertools, + Itertools, + kw, last, + lazy_static, + Lazy, LF: "\n", + Lint, + ln, + lock_api, + log, LowerExp, LowerHex, + macro_use_imports, + map_or_else, + map_or, max, + MAX, + mem, min, + MIN, mode, + module_name_repetitions, msrv, + msrvs, + MsrvStack, + mut_ptr, + mutex, + needless_return, + next_tuple, Octal, + once_cell, + OpenOptions, or_default, + Other, parse, + PathLookup, + paths, + powf, + powi, push, + redundant_pub_crate, regex, + Regex, + RegexBuilder, + RegexSet, reserve, resize, restriction, + rustc_lint_defs, + rustc_lint, + rustc_span, rustfmt_skip, + rwlock, + serde, set_len, set_mode, set_readonly, signum, + single_component_path_imports, + span_lint_and_then, split_whitespace, split, + sqrt, Start, + Step, + style, + symbol, + Symbol, + SyntaxContext, take, TBD, then_some, + to_ascii_lowercase, + to_ascii_uppercase, to_digit, + to_lowercase, to_owned, + to_uppercase, + tokio, + unreachable_pub, + unsafe_removed_from_name, + unused_braces, unused_extern_crates, + unused_import_braces, + unused_trait_names, + unused, unwrap_err, unwrap_or_default, + unwrap_or_else, UpperExp, UpperHex, V4, V6, + Visitor, + warnings, Weak, + wildcard_imports, with_capacity, wrapping_offset, } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index da09edd7f7c0..26d41cfb4977 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -20,7 +20,7 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; @@ -32,7 +32,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; -use crate::{def_path_def_ids, match_def_path, path_res}; +use crate::path_res; +use crate::paths::{PathNS, lookup_path_str}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -229,9 +230,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< /// Checks whether a type implements a trait. /// The function returns false in case the type contains an inference variable. /// -/// See: -/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`]. -/// * [Common tools for writing lints] for an example how to use this function and other options. +/// See [Common tools for writing lints] for an example how to use this function and other options. /// /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait pub fn implements_trait<'tcx>( @@ -359,56 +358,6 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// 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 `normalize` 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: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - is_normalizable_helper(cx, param_env, ty, 0, &mut FxHashMap::default()) -} - -fn is_normalizable_helper<'tcx>( - cx: &LateContext<'tcx>, - param_env: ParamEnv<'tcx>, - ty: Ty<'tcx>, - depth: usize, - cache: &mut FxHashMap, bool>, -) -> bool { - if let Some(&cached_result) = cache.get(&ty) { - return cached_result; - } - if !cx.tcx.recursion_limit().value_within_limit(depth) { - return false; - } - // Prevent recursive loops by answering `true` to recursive requests with the same - // type. This will be adjusted when the outermost call analyzes all the type - // components. - cache.insert(ty, true); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let cause = ObligationCause::dummy(); - let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, args) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), depth + 1, 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, depth + 1, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false - }; - cache.insert(ty, result); - result -} - /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any /// integer or floating-point number type). /// @@ -474,17 +423,6 @@ 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, - } -} - /// Checks if the drop order for a type matters. /// /// Some std types implement drop solely to deallocate memory. For these types, and composites @@ -993,9 +931,6 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option< /// account the layout of type parameters. pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { use rustc_middle::ty::layout::LayoutOf; - if !is_normalizable(cx, cx.param_env, ty) { - return 0; - } match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) { (Ok(size), _) => size, (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(), @@ -1184,10 +1119,7 @@ impl<'tcx> InteriorMut<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self { let ignored_def_ids = ignore_interior_mutability .iter() - .flat_map(|ignored_ty| { - let path: Vec<&str> = ignored_ty.split("::").collect(); - def_path_def_ids(tcx, path.as_slice()) - }) + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) .collect(); Self { @@ -1422,3 +1354,10 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' let mut phantoms = FxHashSet::default(); has_non_owning_mutable_access_inner(cx, &mut phantoms, iter_ty) } + +/// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. +pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_slice() + || ty.is_array() + || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) +} diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 3398ff8af2f5..6e3586623277 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -11,14 +11,14 @@ //! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should //! be considered a bug. -use crate::def_path_res; +use crate::paths::{PathNS, lookup_path}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; mod certainty; use certainty::{Certainty, Meet, join, meet}; @@ -194,7 +194,7 @@ fn path_segment_certainty( path_segment: &PathSegment<'_>, resolves_to_type: bool, ) -> Certainty { - let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) { + let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) { // A definition's type is certain if it refers to something without generics (e.g., a crate or module, or // an unparameterized type), or the generics are instantiated with arguments that are certain. // @@ -267,17 +267,24 @@ fn path_segment_certainty( /// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`. /// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`. -fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option { +fn update_res( + cx: &LateContext<'_>, + parent_certainty: Certainty, + path_segment: &PathSegment<'_>, + resolves_to_type: bool, +) -> Option { if path_segment.res == Res::Err && let Some(def_id) = parent_certainty.to_def_id() { let mut def_path = cx.get_def_path(def_id); def_path.push(path_segment.ident.name); - let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::>()); - if let [res] = reses.as_slice() { Some(*res) } else { None } - } else { - None + let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value }; + if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() { + return Some(Res::Def(cx.tcx.def_kind(id), id)); + } } + + None } #[allow(clippy::cast_possible_truncation)] diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index fe488ef89da1..d4bf6cd48a15 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -6,7 +6,6 @@ // positives. #![feature(iter_collect_into)] -#![feature(let_chains)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 39c7f0e4ad5a..da41bdd27bcb 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-05-01" +channel = "nightly-2025-05-14" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 87ca9c5beddf..f8acf88cf81c 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -1,7 +1,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] -#![feature(let_chains)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 6d391bd622a8..78b27e2f6139 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, let_chains)] +#![feature(rustc_private)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 16a1a415102c..4ac2bd532851 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -44,8 +44,8 @@ fn dogfood() { "rustc_tools_util", ] { println!("linting {package}"); - if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) { - failed_packages.push(if package.is_empty() { "root" } else { package }); + if !run_clippy_for_package(package) { + failed_packages.push(package); } } @@ -57,7 +57,7 @@ fn dogfood() { } #[must_use] -fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { +fn run_clippy_for_package(project: &str) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -79,15 +79,17 @@ fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { } } - command.arg("--").args(args); + command.arg("--"); command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - command.args(["-D", "clippy::dbg_macro"]); - + command.args(["-D", "clippy::all", "-D", "clippy::pedantic", "-D", "clippy::dbg_macro"]); if !cfg!(feature = "internal") { // running a clippy built without internal lints on the clippy source - // that contains e.g. `allow(clippy::invalid_paths)` + // that contains e.g. `allow(clippy::symbol_as_str)` command.args(["-A", "unknown_lints"]); } + // Workaround for not being a workspace, add the crate's directory back to the path + command.args(["--remap-path-prefix", &format!("={project}")]); + command.status().unwrap().success() } diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs index 13cf36823c5e..cb7d61eee24a 100644 --- a/src/tools/clippy/tests/integration.rs +++ b/src/tools/clippy/tests/integration.rs @@ -30,7 +30,7 @@ fn integration_test() { let repo_dir = tempfile::tempdir() .expect("couldn't create temp dir") - .into_path() + .keep() .join(crate_name); let st = Command::new("git") diff --git a/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs b/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs deleted file mode 100644 index f730f564a09c..000000000000 --- a/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(clippy::unnecessary_def_path)] - -pub static OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.rs b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.rs new file mode 100644 index 000000000000..9dc8e9e8f4c1 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.rs @@ -0,0 +1,60 @@ +#![deny(clippy::derive_deserialize_allowing_unknown)] + +use serde::{Deserialize, Deserializer}; + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +struct Struct { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +enum Enum { + A(bool), + B { limit: u64 }, +} + +// negative tests + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct StructWithDenyUnknownFields { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithDenyUnknownFields { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +#[serde(untagged, deny_unknown_fields)] +enum MultipleSerdeAttributes { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +struct TupleStruct(u64, bool); + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithOnlyTupleVariants { + A(bool), + B(u64), +} + +struct ManualSerdeImplementation; + +impl<'de> Deserialize<'de> for ManualSerdeImplementation { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let () = <() as Deserialize>::deserialize(deserializer)?; + Ok(ManualSerdeImplementation) + } +} diff --git a/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.stderr b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.stderr new file mode 100644 index 000000000000..93d64826c993 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.stderr @@ -0,0 +1,23 @@ +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:5:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:1:9 + | +LL | #![deny(clippy::derive_deserialize_allowing_unknown)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:11:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/interning_literals.stderr b/src/tools/clippy/tests/ui-internal/interning_literals.stderr index 628b97eff84d..9ff4194e542b 100644 --- a/src/tools/clippy/tests/ui-internal/interning_literals.stderr +++ b/src/tools/clippy/tests/ui-internal/interning_literals.stderr @@ -4,9 +4,10 @@ error: interning a string literal LL | let _ = Symbol::intern("f32"); | ^^^^^^^^^^^^^^^^^^^^^ | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::interning-literals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` -help: use the preinterned symbol +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("f32"); LL + let _ = sym::f32; @@ -18,7 +19,8 @@ error: interning a string literal LL | let _ = Symbol::intern("proc-macro"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("proc-macro"); LL + let _ = sym::proc_dash_macro; @@ -30,7 +32,8 @@ error: interning a string literal LL | let _ = Symbol::intern("self"); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("self"); LL + let _ = kw::SelfLower; @@ -42,7 +45,8 @@ error: interning a string literal LL | let _ = Symbol::intern("msrv"); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("msrv"); LL + let _ = sym::msrv; @@ -54,7 +58,8 @@ error: interning a string literal LL | let _ = Symbol::intern("Cargo.toml"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("Cargo.toml"); LL + let _ = sym::Cargo_toml; diff --git a/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr index 8294453a8f94..879d9e633c23 100644 --- a/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr +++ b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr @@ -4,9 +4,10 @@ error: interning a string literal LL | let _ = Symbol::intern("xyz123"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::interning-literals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` -help: add the symbol to `clippy_utils/src/sym.rs` and use it +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("xyz123"); LL + let _ = sym::xyz123; @@ -18,7 +19,8 @@ error: interning a string literal LL | let _ = Symbol::intern("with-dash"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("with-dash"); LL + let _ = sym::with_dash; @@ -30,7 +32,8 @@ error: interning a string literal LL | let _ = Symbol::intern("with.dot"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("with.dot"); LL + let _ = sym::with_dot; diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.rs b/src/tools/clippy/tests/ui-internal/invalid_paths.rs deleted file mode 100644 index 7317abc2185a..000000000000 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![deny(clippy::invalid_paths)] -#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] - -mod paths { - // Good path - pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; - - // Path to method on inherent impl of a primitive type - pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; - - // Path to method on inherent impl - pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; - - // Path with empty segment - pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - //~^ invalid_paths - - // Path with bad crate - pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - //~^ invalid_paths - - // Path with bad module - pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - //~^ invalid_paths - - // Path to method on an enum inherent impl - pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr deleted file mode 100644 index 7b7b25ce8d8d..000000000000 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: invalid path - --> tests/ui-internal/invalid_paths.rs:15:5 - | -LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> tests/ui-internal/invalid_paths.rs:1:9 - | -LL | #![deny(clippy::invalid_paths)] - | ^^^^^^^^^^^^^^^^^^^^^ - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:19:5 - | -LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:23:5 - | -LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed b/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed index 3e26732836ca..6a71b16c6049 100644 --- a/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed @@ -18,4 +18,11 @@ fn f(s: Symbol) { //~^ symbol_as_str sym::get == s; //~^ symbol_as_str + + let _ = match s { + //~^ symbol_as_str + sym::unwrap_err => 1, + sym::unwrap_or_default | sym::unwrap_or_else => 2, + _ => 3, + }; } diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.rs b/src/tools/clippy/tests/ui-internal/symbol_as_str.rs index 334c32d18983..43136504bf1a 100644 --- a/src/tools/clippy/tests/ui-internal/symbol_as_str.rs +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.rs @@ -18,4 +18,11 @@ fn f(s: Symbol) { //~^ symbol_as_str "get" == s.as_str(); //~^ symbol_as_str + + let _ = match s.as_str() { + //~^ symbol_as_str + "unwrap_err" => 1, + "unwrap_or_default" | "unwrap_or_else" => 2, + _ => 3, + }; } diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr b/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr index 39f81f3833c4..3eeead4aa8c1 100644 --- a/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr @@ -4,9 +4,10 @@ error: converting a Symbol to a string LL | s.as_str() == "f32"; | ^^^^^^^^^^ | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::symbol-as-str` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` -help: use the preinterned symbol +help: use preinterned symbols instead | LL - s.as_str() == "f32"; LL + s == sym::f32; @@ -18,7 +19,8 @@ error: converting a Symbol to a string LL | s.as_str() == "proc-macro"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "proc-macro"; LL + s == sym::proc_dash_macro; @@ -30,7 +32,8 @@ error: converting a Symbol to a string LL | s.as_str() == "self"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "self"; LL + s == kw::SelfLower; @@ -42,7 +45,8 @@ error: converting a Symbol to a string LL | s.as_str() == "msrv"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "msrv"; LL + s == sym::msrv; @@ -54,7 +58,8 @@ error: converting a Symbol to a string LL | s.as_str() == "Cargo.toml"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "Cargo.toml"; LL + s == sym::Cargo_toml; @@ -66,11 +71,27 @@ error: converting a Symbol to a string LL | "get" == s.as_str(); | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - "get" == s.as_str(); LL + sym::get == s; | -error: aborting due to 6 previous errors +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:22:19 + | +LL | let _ = match s.as_str() { + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL ~ let _ = match s { +LL | +LL ~ sym::unwrap_err => 1, +LL ~ sym::unwrap_or_default | sym::unwrap_or_else => 2, + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr index 5349983ca519..65664ebb451a 100644 --- a/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr @@ -4,9 +4,10 @@ error: converting a Symbol to a string LL | s.as_str() == "xyz123"; | ^^^^^^^^^^ | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::symbol-as-str` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` -help: add the symbol to `clippy_utils/src/sym.rs` and use it +help: use preinterned symbols instead | LL - s.as_str() == "xyz123"; LL + s == sym::xyz123; @@ -18,7 +19,8 @@ error: converting a Symbol to a string LL | s.as_str() == "with-dash"; | ^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "with-dash"; LL + s == sym::with_dash; @@ -30,7 +32,8 @@ error: converting a Symbol to a string LL | s.as_str() == "with.dot"; | ^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "with.dot"; LL + s == sym::with_dot; diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed deleted file mode 100644 index 89902ebe4e54..000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed +++ /dev/null @@ -1,77 +0,0 @@ -//@aux-build:paths.rs -#![deny(clippy::unnecessary_def_path)] -#![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] - -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; - -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; - -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; - -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; - -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = is_type_diagnostic_item(cx, ty, sym::Rc); - //~^ unnecessary_def_path - - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - - let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); - //~^ unnecessary_def_path - - let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); - //~^ unnecessary_def_path - let _ = cx.tcx.is_diagnostic_item(sym::Option, did); - //~^ unnecessary_def_path - let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); - //~^ unnecessary_def_path - - let _ = is_trait_method(cx, expr, sym::AsRef); - //~^ unnecessary_def_path - - let _ = is_path_diagnostic_item(cx, expr, sym::Option); - //~^ unnecessary_def_path - let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); - //~^ unnecessary_def_path - let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); - //~^ unnecessary_def_path -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs index cfca15267c19..5cd3254188d9 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs @@ -1,77 +1,20 @@ -//@aux-build:paths.rs -#![deny(clippy::unnecessary_def_path)] #![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{macro_path, sym, type_path, value_path}; -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; +static OPTION: PathLookup = type_path!(core::option::Option); +//~^ unnecessary_def_path +static SOME: PathLookup = type_path!(core::option::Option::Some); +//~^ unnecessary_def_path -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; +static RESULT: PathLookup = type_path!(core::result::Result); +//~^ unnecessary_def_path +static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); +//~^ unnecessary_def_path -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); +//~^ unnecessary_def_path -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = match_type(cx, ty, &OPTION); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, RESULT); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, &["core", "result", "Result"]); - //~^ unnecessary_def_path - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - //~^ unnecessary_def_path - - let _ = match_type(cx, ty, &paths::OPTION); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, paths::RESULT); - //~^ unnecessary_def_path - - let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - //~^ unnecessary_def_path - - let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - //~^ unnecessary_def_path - let _ = match_def_path(cx, did, &["core", "option", "Option"]); - //~^ unnecessary_def_path - let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - //~^ unnecessary_def_path - - let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - //~^ unnecessary_def_path - - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - //~^ unnecessary_def_path - let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - //~^ unnecessary_def_path - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); - //~^ unnecessary_def_path -} - -fn main() {} +static VEC_MACRO: PathLookup = macro_path!(std::vec); +//~^ unnecessary_def_path diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr index d7fb4ea551e1..4abb1be7406c 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr @@ -1,100 +1,58 @@ -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:37:13 +error: a diagnostic name exists for this path: sym::Option + --> tests/ui-internal/unnecessary_def_path.rs:6:29 | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` +LL | static OPTION: PathLookup = type_path!(core::option::Option); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui-internal/unnecessary_def_path.rs:2:9 - | -LL | #![deny(clippy::unnecessary_def_path)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:39:13 +error: a language item exists for this path: LangItem::OptionSome + --> tests/ui-internal/unnecessary_def_path.rs:8:27 | -LL | let _ = match_type(cx, ty, RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:41:13 +LL | static SOME: PathLookup = type_path!(core::option::Option::Some); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:46:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:11:29 | -LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:49:13 +LL | static RESULT: PathLookup = type_path!(core::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &paths::OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:51:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:13:37 | -LL | let _ = match_type(cx, ty, paths::RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:54:13 +LL | static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:56:13 +error: a diagnostic name exists for this path: sym::vec_new + --> tests/ui-internal/unnecessary_def_path.rs:16:30 | -LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:59:13 +LL | static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:61:13 +error: a diagnostic name exists for this path: sym::vec_macro + --> tests/ui-internal/unnecessary_def_path.rs:19:32 | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:63:13 +LL | static VEC_MACRO: PathLookup = macro_path!(std::vec); + | ^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` - | - = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:66:13 - | -LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:69:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:71:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:73:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` - -error: aborting due to 15 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs deleted file mode 100644 index bd7a55114acb..000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![feature(rustc_private)] -#![allow(unused)] -#![deny(clippy::unnecessary_def_path)] - -extern crate rustc_hir; - -use rustc_hir::LangItem; - -fn main() { - const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - //~^ unnecessary_def_path - const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - //~^ unnecessary_def_path - const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - //~^ unnecessary_def_path - - // Don't lint, not a diagnostic or language item - const OPS_MOD: [&str; 2] = ["core", "ops"]; -} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr deleted file mode 100644 index 88fdf6f1c188..000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:10:36 - | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::Deref` -note: the lint level is defined here - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:3:9 - | -LL | #![deny(clippy::unnecessary_def_path)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: hardcoded path to a language item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40 - | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `LangItem::DerefMut` - -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:14:43 - | -LL | const OPS_MOD: [&str; 5] = ["core", "ops"]; - | ^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::deref_method` - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed index f12273954c6d..5e189471b000 100644 --- a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::collapsible_if)] fn main() { diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs index 5a984d7a3cbe..525eebf632a0 100644 --- a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::collapsible_if)] fn main() { diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr index c22a65a44730..c9de166a969a 100644 --- a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr @@ -1,5 +1,5 @@ error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:5:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:4:5 | LL | / if let Some(a) = Some(3) { LL | | // with comment @@ -21,7 +21,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:13:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:12:5 | LL | / if let Some(a) = Some(3) { LL | | // with comment @@ -41,7 +41,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:21:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:20:5 | LL | / if Some(3) == Some(4).map(|x| x - 1) { LL | | // with comment diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml new file mode 100644 index 000000000000..3ed7cedbd147 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml @@ -0,0 +1 @@ +allow-exact-repetitions = false diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs new file mode 100644 index 000000000000..20603766624c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs @@ -0,0 +1,13 @@ +#![warn(clippy::module_name_repetitions)] +#![allow(dead_code)] + +pub mod foo { + // this line should produce a warning: + pub fn foo() {} + //~^ module_name_repetitions + + // but this line shouldn't + pub fn to_foo() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr new file mode 100644 index 000000000000..8e6f726d02c0 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr @@ -0,0 +1,11 @@ +error: item name is the same as its containing module's name + --> tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs:6:12 + | +LL | pub fn foo() {} + | ^^^ + | + = note: `-D clippy::module-name-repetitions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/clippy.toml b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/clippy.toml new file mode 100644 index 000000000000..2fe64b2755b6 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/clippy.toml @@ -0,0 +1 @@ +missing-docs-allow-unused = true diff --git a/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs new file mode 100644 index 000000000000..155f680c7b13 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs @@ -0,0 +1,26 @@ +//! Test file for missing_docs_in_private_items lint with allow_unused configuration +#![warn(clippy::missing_docs_in_private_items)] +#![allow(dead_code)] + +/// A struct with some documented and undocumented fields +struct Test { + /// This field is documented + field1: i32, + _unused: i32, // This should not trigger a warning because it starts with an underscore + field3: i32, //~ missing_docs_in_private_items +} + +struct Test2 { + //~^ missing_docs_in_private_items + _field1: i32, // This should not trigger a warning + _field2: i32, // This should not trigger a warning +} + +struct Test3 { + //~^ missing_docs_in_private_items + /// This field is documented although this is not mandatory + _unused: i32, // This should not trigger a warning because it starts with an underscore + field2: i32, //~ missing_docs_in_private_items +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr new file mode 100644 index 000000000000..8f511883e900 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr @@ -0,0 +1,38 @@ +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:10:5 + | +LL | field3: i32, + | ^^^^^^^^^^^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:13:1 + | +LL | / struct Test2 { +LL | | +LL | | _field1: i32, // This should not trigger a warning +LL | | _field2: i32, // This should not trigger a warning +LL | | } + | |_^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:19:1 + | +LL | / struct Test3 { +LL | | +LL | | /// This field is documented although this is not mandatory +LL | | _unused: i32, // This should not trigger a warning because it starts with an underscore +LL | | field2: i32, +LL | | } + | |_^ + +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:23:5 + | +LL | field2: i32, + | ^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml index 41dbd5068479..c7a326f28295 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -14,4 +14,7 @@ disallowed-methods = [ "conf_disallowed_methods::Struct::method", "conf_disallowed_methods::Trait::provided_method", "conf_disallowed_methods::Trait::implemented_method", + # re-exports + "conf_disallowed_methods::identity", + "conf_disallowed_methods::renamed", ] diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index dd170d6baf85..2dac01649a0f 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -8,6 +8,9 @@ extern crate regex; use futures::stream::{empty, select_all}; use regex::Regex; +use std::convert::identity; +use std::hint::black_box as renamed; + fn local_fn() {} struct Struct; @@ -71,4 +74,9 @@ fn main() { //~^ disallowed_methods s.implemented_method(); //~^ disallowed_methods + + identity(()); + //~^ disallowed_methods + renamed(1); + //~^ disallowed_methods } diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index f7dda81eb936..20474ad6e927 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:33:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `regex::Regex::is_match` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:35:8 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:38:8 | LL | re.is_match("abc"); | ^^^^^^^^ @@ -16,76 +16,88 @@ LL | re.is_match("abc"); = note: no matching allowed error: use of a disallowed method `std::iter::Iterator::sum` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:14 | LL | a.iter().sum::(); | ^^^ error: use of a disallowed method `slice::sort_unstable` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:45:7 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:46:20 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:49:20 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:50:61 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:53:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:28 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:57:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:53 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:31 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_fn` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:66:5 | LL | local_fn(); | ^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_mod::f` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:65:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:5 | LL | local_mod::f(); | ^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Struct::method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:71:7 | LL | s.method(); | ^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:70:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:73:7 | LL | s.provided_method(); | ^^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:72:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:75:7 | LL | s.implemented_method(); | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: use of a disallowed method `conf_disallowed_methods::identity` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:78:5 + | +LL | identity(()); + | ^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::renamed` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:80:5 + | +LL | renamed(1); + | ^^^^^^^ + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml index 6cb9e2ef9546..08e35017f782 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml @@ -6,7 +6,7 @@ disallowed-types = [ "std::thread::Thread", "std::time::Instant", "std::io::Read", - "std::primitive::usize", + "usize", "bool", # can give path and reason with an inline table { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr index 18bc36ca1e33..061cdc7649ad 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr @@ -37,7 +37,7 @@ error: use of a disallowed type `std::io::Read` LL | fn trait_obj(_: &dyn std::io::Read) {} | ^^^^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:33 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} @@ -49,13 +49,13 @@ error: use of a disallowed type `bool` LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:28 | LL | fn const_generics() {} | ^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:33:24 | LL | struct GenArg([u8; U]); @@ -123,7 +123,7 @@ error: use of a disallowed type `proc_macro2::Ident` LL | let _ = syn::Ident::new("", todo!()); | ^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:61:12 | LL | let _: usize = 64_usize; diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml index 6d0d732a9223..997ed47b71cb 100644 --- a/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml @@ -1,12 +1,15 @@ -[[disallowed-types]] -path = "std::result::Result::Err" - [[disallowed-macros]] path = "bool" [[disallowed-methods]] path = "std::process::current_exe" +[[disallowed-methods]] +path = "" + +[[disallowed-types]] +path = "std::result::Result::Err" + # negative test [[disallowed-methods]] diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs index c15203827034..ff4eada39007 100644 --- a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs @@ -1,5 +1,6 @@ //@error-in-other-file: expected a macro, found a primitive type -//@error-in-other-file: `std::process::current_exe` does not refer to an existing function -//@error-in-other-file: expected a type, found a tuple variant +//@error-in-other-file: `std::process::current_exe` does not refer to a reachable function +//@error-in-other-file: `` does not refer to a reachable function +//@error-in-other-file: expected a type, found a variant fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr index 82550108eba5..59a427dc99ca 100644 --- a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr @@ -1,23 +1,38 @@ warning: expected a macro, found a primitive type - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 | LL | / [[disallowed-macros]] LL | | path = "bool" | |_____________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: `std::process::current_exe` does not refer to an existing function - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 +warning: `std::process::current_exe` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 | LL | / [[disallowed-methods]] LL | | path = "std::process::current_exe" | |__________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: expected a type, found a tuple variant - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 +warning: `` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 + | +LL | / [[disallowed-methods]] +LL | | path = "" + | |_________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: expected a type, found a variant + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:10:1 | LL | / [[disallowed-types]] LL | | path = "std::result::Result::Err" | |_________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: 3 warnings emitted +warning: 4 warnings emitted diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml new file mode 100644 index 000000000000..82560cfd5e2a --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml @@ -0,0 +1,4 @@ +# In the following configuration, "recommendation" should be "reason" or "replacement". +disallowed-macros = [ + { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, +] diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs new file mode 100644 index 000000000000..9c770c31f6f8 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs @@ -0,0 +1,5 @@ +#[rustfmt::skip] +//@error-in-other-file: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum +fn main() { + panic!(); +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr new file mode 100644 index 000000000000..b564709721d5 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum + --> $DIR/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml:3:5 + | +LL | { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f2eaa66a4ae4..6ee77ebd8ece 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -5,6 +5,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -57,6 +58,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings @@ -97,6 +99,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -149,6 +152,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings @@ -189,6 +193,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -241,6 +246,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings diff --git a/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/clippy.toml new file mode 100644 index 000000000000..e664256d2a2f --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/clippy.toml @@ -0,0 +1,10 @@ +# The first two `disallowed-methods` paths should generate warnings, but the third should not. + +[[disallowed-methods]] +path = "regex::Regex::new_" + +[[disallowed-methods]] +path = "regex::Regex_::new" + +[[disallowed-methods]] +path = "regex_::Regex::new" diff --git a/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs new file mode 100644 index 000000000000..14f15e733111 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs @@ -0,0 +1,6 @@ +//@error-in-other-file: `regex::Regex::new_` does not refer to a reachable function +//@error-in-other-file: `regex::Regex_::new` does not refer to a reachable function + +extern crate regex; + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr new file mode 100644 index 000000000000..e5fd548b26df --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr @@ -0,0 +1,20 @@ +warning: `regex::Regex::new_` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:3:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex::new_" + | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: `regex::Regex_::new` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:6:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex_::new" + | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: 2 warnings emitted + diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs index 7f93d2071c9d..b60cb7632e20 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -12,7 +12,7 @@ fn f2() where T: Copy + Clone + Sync + Send + ?Sized, T: Unpin + PartialEq, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr index c5102c39d1cf..ba0f41167a00 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui-toml/type_repetition_in_bounds/main.rs:14:5 | LL | T: Unpin + PartialEq, diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout index eed704e82fe1..88a275302387 100644 --- a/src/tools/clippy/tests/ui/author.stdout +++ b/src/tools/clippy/tests/ui/author.stdout @@ -1,8 +1,6 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Cast(expr, cast_ty) = init.kind - && let TyKind::Path(ref qpath) = cast_ty.kind - && match_qpath(qpath, &["char"]) && let ExprKind::Lit(ref lit) = expr.kind && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index 54325f9776c5..e453299edbcf 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -14,8 +14,6 @@ if let ExprKind::Block(block, None) = expr.kind && name1.as_str() == "_t" && let StmtKind::Semi(e) = block.stmts[2].kind && let ExprKind::Unary(UnOp::Neg, inner) = e.kind - && let ExprKind::Path(ref qpath) = inner.kind - && match_qpath(qpath, &["x"]) && block.expr.is_none() { // report your lint here @@ -25,18 +23,14 @@ if let ExprKind::Block(block, None) = expr.kind && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["String", "new"]) + && is_path_diagnostic_item(cx, func, sym::string_new) && args.is_empty() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "expr" && let Some(trailing_expr) = block.expr && let ExprKind::Call(func1, args1) = trailing_expr.kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["drop"]) + && is_path_diagnostic_item(cx, func1, sym::mem_drop) && args1.len() == 1 - && let ExprKind::Path(ref qpath2) = args1[0].kind - && match_qpath(qpath2, &["expr"]) { // report your lint here } diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout index 59d4da490fe5..2b179d45112e 100644 --- a/src/tools/clippy/tests/ui/author/call.stdout +++ b/src/tools/clippy/tests/ui/author/call.stdout @@ -1,8 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && is_path_diagnostic_item(cx, func, sym::cmp_min) && args.len() == 2 && let ExprKind::Lit(ref lit) = args[0].kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index 8ffdf8862027..da359866bffc 100644 --- a/src/tools/clippy/tests/ui/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -31,10 +31,8 @@ if let StmtKind::Let(local) = stmt.kind if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind && let PatKind::Expr(lit_expr) = let_expr.pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.init.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = then.kind && block.stmts.is_empty() && block.expr.is_none() diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout index a5a8c0304ee4..f02ea5bf075f 100644 --- a/src/tools/clippy/tests/ui/author/issue_3849.stdout +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout @@ -1,11 +1,8 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["std", "mem", "transmute"]) + && is_path_diagnostic_item(cx, func, sym::transmute) && args.len() == 1 - && let ExprKind::Path(ref qpath1) = args[0].kind - && match_qpath(qpath1, &["ZPTR"]) && let PatKind::Wild = local.pat.kind { // report your lint here diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index c94eb171f52b..79794cec9269 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -14,8 +14,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block.stmts.len() == 1 && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init - && let ExprKind::Path(ref qpath1) = init.kind - && match_qpath(qpath1, &["y"]) && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind && name1.as_str() == "z" && block.expr.is_none() @@ -64,8 +62,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo // report your lint here } if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) - && let ExprKind::Path(ref qpath) = condition.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = body.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind @@ -77,10 +73,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) && let PatKind::Expr(lit_expr) = let_pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = if_then.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout index 3186d0cbc276..5f8a4ce23630 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout @@ -7,12 +7,10 @@ if let StmtKind::Let(local) = stmt.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind && let ExprKind::Call(func, args) = e.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -27,12 +25,9 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath2) = func2.kind - && match_qpath(qpath2, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath3) = inner2.kind - && match_qpath(qpath3, &["x"]) && block.expr.is_none() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "print_text" diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout index 3f9be297c33c..ecc252543117 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout @@ -17,12 +17,10 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block1.stmts.len() == 1 && let StmtKind::Semi(e1) = block1.stmts[0].kind && let ExprKind::Call(func, args) = e1.kind - && let ExprKind::Path(ref qpath1) = func.kind - && match_qpath(qpath1, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath2) = func1.kind - && match_qpath(qpath2, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -37,12 +35,9 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath3) = func2.kind - && match_qpath(qpath3, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath4) = inner2.kind - && match_qpath(qpath4, &["i"]) && block1.expr.is_none() && block.expr.is_none() { diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index acb3b140dfa1..9752d7a9f99d 100644 --- a/src/tools/clippy/tests/ui/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 && let PatKind::Expr(lit_expr) = arms[0].pat.kind - && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node && let PatKind::Expr(lit_expr1) = arms[1].pat.kind - && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind + && let PatExprKind::Lit { ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind @@ -23,8 +23,6 @@ if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind && name.as_str() == "x" && let Some(trailing_expr) = block.expr - && let ExprKind::Path(ref qpath) = trailing_expr.kind - && match_qpath(qpath, &["x"]) && let PatKind::Wild = arms[2].pat.kind && arms[2].guard.is_none() && let ExprKind::Lit(ref lit5) = arms[2].body.kind diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout index b66bbccb3cf1..1e8fbafd30c5 100644 --- a/src/tools/clippy/tests/ui/author/struct.stdout +++ b/src/tools/clippy/tests/ui/author/struct.stdout @@ -1,5 +1,4 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind @@ -20,11 +19,10 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind // report your lint here } if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let PatKind::Expr(lit_expr) = fields[0].pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -34,10 +32,9 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind // report your lint here } if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind - && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 && let PatKind::Expr(lit_expr) = fields[0].kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -48,8 +45,6 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind } if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind && method_name.ident.as_str() == "test" - && let ExprKind::Path(ref qpath) = receiver.kind - && match_qpath(qpath, &["test_method_call"]) && args.is_empty() { // report your lint here diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs index e72d6b6ceadf..4c61c5accd39 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs @@ -51,14 +51,14 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { let arg = sig.inputs.first_mut()?; - if let FnArg::Typed(PatType { pat, .. }) = arg { - if let Pat::Ident(PatIdent { ident, .. }) = &**pat { - if ident == "self" { - return Some(arg); - } - } + if let FnArg::Typed(PatType { pat, .. }) = arg + && let Pat::Ident(PatIdent { ident, .. }) = &**pat + && ident == "self" + { + Some(arg) + } else { + None } - None } let mut elided = 0; @@ -66,30 +66,29 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea // Look for methods having arbitrary self type taken by &mut ref for inner in &mut item.items { - if let ImplItem::Fn(method) = inner { - if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { - if let box Type::Reference(reference) = &mut pat_type.ty { - // Target only unnamed lifetimes - let name = match &reference.lifetime { - Some(lt) if lt.ident == "_" => make_name(elided), - None => make_name(elided), - _ => continue, - }; - elided += 1; + if let ImplItem::Fn(method) = inner + && let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) + && let box Type::Reference(reference) = &mut pat_type.ty + { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; - // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. - // In order to avoid adding the dependency, get a default span from a nonexistent token. - // A default span is needed to mark the code as coming from expansion. - let span = Star::default().span(); + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a nonexistent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); - // Replace old lifetime with the named one - let lifetime = Lifetime::new(&name, span); - reference.lifetime = Some(parse_quote!(#lifetime)); + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); - // Add lifetime to the generics of the method - method.sig.generics.params.push(parse_quote!(#lifetime)); - } - } + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); } } @@ -129,15 +128,15 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream let mut async_fn = parse_macro_input!(input as syn::ItemFn); for stmt in &mut async_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt { - if let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() { - let blc = quote_spanned!( await_token.span => { - #[allow(clippy::let_and_return)] - let __pinned = #base; - __pinned - }); - *scrutinee = parse_quote!(#blc); - } + if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt + && let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() + { + let blc = quote_spanned!( await_token.span => { + #[allow(clippy::let_and_return)] + let __pinned = #base; + __pinned + }); + *scrutinee = parse_quote!(#blc); } } diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs index 7a4cc4fa9ee8..bb55539617fc 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![feature(proc_macro_span)] #![allow(clippy::needless_if, dead_code)] diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed index ed6141244b40..7fa7c016f935 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs index 3f1f1c766e46..2295d6f1362d 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr index 94089bc6dc8e..e4ae57304141 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr @@ -1,5 +1,5 @@ error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:14:5 + --> tests/ui/bool_to_int_with_if.rs:13:5 | LL | / if a { LL | | @@ -14,7 +14,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::bool_to_int_with_if)]` error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:20:5 + --> tests/ui/bool_to_int_with_if.rs:19:5 | LL | / if a { LL | | @@ -27,7 +27,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:26:5 + --> tests/ui/bool_to_int_with_if.rs:25:5 | LL | / if !a { LL | | @@ -40,7 +40,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:32:5 + --> tests/ui/bool_to_int_with_if.rs:31:5 | LL | / if a || b { LL | | @@ -53,7 +53,7 @@ LL | | }; = note: `(a || b) as i32` or `(a || b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:38:5 + --> tests/ui/bool_to_int_with_if.rs:37:5 | LL | / if cond(a, b) { LL | | @@ -66,7 +66,7 @@ LL | | }; = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:44:5 + --> tests/ui/bool_to_int_with_if.rs:43:5 | LL | / if x + y < 4 { LL | | @@ -79,7 +79,7 @@ LL | | }; = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:54:12 + --> tests/ui/bool_to_int_with_if.rs:53:12 | LL | } else if b { | ____________^ @@ -93,7 +93,7 @@ LL | | }; = note: `b as i32` or `b.into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:64:12 + --> tests/ui/bool_to_int_with_if.rs:63:12 | LL | } else if b { | ____________^ @@ -107,7 +107,7 @@ LL | | }; = note: `!b as i32` or `(!b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:130:5 + --> tests/ui/bool_to_int_with_if.rs:129:5 | LL | if a { 1 } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` @@ -115,7 +115,7 @@ LL | if a { 1 } else { 0 } = note: `a as u8` or `a.into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:174:13 + --> tests/ui/bool_to_int_with_if.rs:173:13 | LL | let _ = if dbg!(4 > 0) { 1 } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(dbg!(4 > 0))` @@ -123,7 +123,7 @@ LL | let _ = if dbg!(4 > 0) { 1 } else { 0 }; = note: `dbg!(4 > 0) as i32` or `dbg!(4 > 0).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:177:18 + --> tests/ui/bool_to_int_with_if.rs:176:18 | LL | let _ = dbg!(if 4 > 0 { 1 } else { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(4 > 0)` 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 4101897d3800..ba0d36d85fe5 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -188,6 +188,91 @@ fn issue11371() { } } +fn gen_option() -> Option<()> { + Some(()) + // Or None +} + +fn gen_result() -> Result<(), ()> { + Ok(()) + // Or Err(()) +} + +fn issue14725() { + let option = Some(()); + + if option.is_some() { + let _ = option.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _ = option.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + let _y = 1; + result.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _y = 1; + result.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let mut option = Some(()); + if option.is_some() { + option = gen_option(); + option.as_mut().unwrap(); + } else { + option = gen_option(); + option.as_mut().unwrap(); + } + + let mut result = Ok::<(), ()>(()); + if result.is_ok() { + result = gen_result(); + result.as_mut().unwrap(); + } else { + result = gen_result(); + result.as_mut().unwrap(); + } +} + +fn issue14763(x: Option, r: Result<(), ()>) { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + }; + _ = || { + if r.is_ok() { + _ = r.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + _ = r.as_ref().unwrap(); + //~^ panicking_unwrap + } + }; +} + +const ISSUE14763: fn(Option) = |x| { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + } +}; + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr index ad3c420270c1..a4bf00992445 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -236,6 +236,92 @@ LL | if result.is_ok() { LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:205:17 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = &option` +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:208:17 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:216:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = &result` +LL | let _y = 1; +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:220:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:246:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:249:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: called `unwrap` on `r` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:255:17 + | +LL | if r.is_ok() { + | ------------ help: try: `if let Ok() = &r` +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:258:17 + | +LL | if r.is_ok() { + | --------- because of this check +... +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:267:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:270:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | @@ -246,5 +332,5 @@ LL | if X.is_some() { = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[deny(static_mut_refs)]` on by default -error: aborting due to 26 previous errors +error: aborting due to 36 previous errors diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed new file mode 100644 index 000000000000..818c6e23259e --- /dev/null +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = std::slice::from_ref(data_ref); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = std::slice::from_ref(&Data); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = std::slice::from_ref(&Point(0, 0)); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs new file mode 100644 index 000000000000..9517dbfd1569 --- /dev/null +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = &[data_ref.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = &[Data.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = &[Point(0, 0).clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr new file mode 100644 index 000000000000..6a31d8782395 --- /dev/null +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr @@ -0,0 +1,23 @@ +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:10:17 + | +LL | let _ = &[data_ref.clone()]; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(data_ref)` + | + = note: `-D clippy::cloned-ref-to-slice-refs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cloned_ref_to_slice_refs)]` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:14:17 + | +LL | let _ = &[Data.clone()]; + | ^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Data)` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:21:17 + | +LL | let _ = &[Point(0, 0).clone()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Point(0, 0))` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed index e1ceb04f9cb8..b553182a4454 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -101,27 +101,8 @@ fn main() { } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 if matches!(true, true) && matches!(true, true) {} @@ -162,3 +143,14 @@ fn layout_check() -> u32 { ; 3 //~^^^^^ collapsible_if } + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; +} diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs index 0b996dca22e8..f5998457ca6c 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -108,27 +108,8 @@ fn main() { } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 if matches!(true, true) { if matches!(true, true) {} @@ -172,3 +153,14 @@ fn layout_check() -> u32 { }; 3 //~^^^^^ collapsible_if } + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; +} diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr index 532811462393..32c6b0194030 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -127,7 +127,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:133:5 + --> tests/ui/collapsible_if.rs:114:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} @@ -141,7 +141,7 @@ LL ~ && matches!(true, true) {} | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:139:5 + --> tests/ui/collapsible_if.rs:120:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} @@ -155,7 +155,7 @@ LL ~ && matches!(true, true) {} | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:151:5 + --> tests/ui/collapsible_if.rs:132:5 | LL | / if true { LL | | if true { @@ -173,7 +173,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:168:5 + --> tests/ui/collapsible_if.rs:149:5 | LL | / if true { LL | | if true { diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.fixed b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.fixed new file mode 100644 index 000000000000..ad08c21ba9ed --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.fixed @@ -0,0 +1,68 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && let Some(b) = Some(4) { + let _ = a + b; + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && a + 1 == 4 { + let _ = a; + } + + //~[edition2024]v collapsible_if + if Some(3) == Some(4).map(|x| x - 1) + && let Some(b) = Some(4) { + let _ = b; + } + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 + && truth() {} + + // Suffix: + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 {} + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 + && truth() {} +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 + && true {} +} diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.stderr b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.stderr new file mode 100644 index 000000000000..b0aa3cecadb6 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.stderr @@ -0,0 +1,132 @@ +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:17:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:24:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:31:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:43:5 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:49:5 + | +LL | / if truth() { +LL | | if let 0 = 1 {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:56:5 + | +LL | / if truth() { +LL | | if let 0 = 1 { +LL | | if truth() {} +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 { +LL | if truth() {} +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:57:9 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_________^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:73:5 + | +LL | / if let 0 = 1 { +LL | | if true {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && true {} + | + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.fixed b/src/tools/clippy/tests/ui/collapsible_if_let_chains.fixed deleted file mode 100644 index 3dd9498a4c9f..000000000000 --- a/src/tools/clippy/tests/ui/collapsible_if_let_chains.fixed +++ /dev/null @@ -1,29 +0,0 @@ -#![feature(let_chains)] -#![warn(clippy::collapsible_if)] - -fn main() { - if let Some(a) = Some(3) { - // with comment, so do not lint - if let Some(b) = Some(4) { - let _ = a + b; - } - } - - if let Some(a) = Some(3) - && let Some(b) = Some(4) { - let _ = a + b; - } - //~^^^^^ collapsible_if - - if let Some(a) = Some(3) - && a + 1 == 4 { - let _ = a; - } - //~^^^^^ collapsible_if - - if Some(3) == Some(4).map(|x| x - 1) - && let Some(b) = Some(4) { - let _ = b; - } - //~^^^^^ collapsible_if -} diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs b/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs index 064b9a0be484..b2e88b1a5568 100644 --- a/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs @@ -1,4 +1,8 @@ -#![feature(let_chains)] +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + #![warn(clippy::collapsible_if)] fn main() { @@ -9,24 +13,64 @@ fn main() { } } + //~[edition2024]v collapsible_if if let Some(a) = Some(3) { if let Some(b) = Some(4) { let _ = a + b; } } - //~^^^^^ collapsible_if + //~[edition2024]v collapsible_if if let Some(a) = Some(3) { if a + 1 == 4 { let _ = a; } } - //~^^^^^ collapsible_if + //~[edition2024]v collapsible_if if Some(3) == Some(4).map(|x| x - 1) { if let Some(b) = Some(4) { let _ = b; } } - //~^^^^^ collapsible_if + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 { + if truth() {} + } + + // Suffix: + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 {} + } + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 { + if truth() {} + } + } +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 { + if true {} + } } diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.stderr b/src/tools/clippy/tests/ui/collapsible_if_let_chains.stderr deleted file mode 100644 index 64a88114c47a..000000000000 --- a/src/tools/clippy/tests/ui/collapsible_if_let_chains.stderr +++ /dev/null @@ -1,58 +0,0 @@ -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:12:5 - | -LL | / if let Some(a) = Some(3) { -LL | | if let Some(b) = Some(4) { -LL | | let _ = a + b; -LL | | } -LL | | } - | |_____^ - | - = note: `-D clippy::collapsible-if` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` -help: collapse nested if block - | -LL ~ if let Some(a) = Some(3) -LL ~ && let Some(b) = Some(4) { -LL | let _ = a + b; -LL ~ } - | - -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:19:5 - | -LL | / if let Some(a) = Some(3) { -LL | | if a + 1 == 4 { -LL | | let _ = a; -LL | | } -LL | | } - | |_____^ - | -help: collapse nested if block - | -LL ~ if let Some(a) = Some(3) -LL ~ && a + 1 == 4 { -LL | let _ = a; -LL ~ } - | - -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:26:5 - | -LL | / if Some(3) == Some(4).map(|x| x - 1) { -LL | | if let Some(b) = Some(4) { -LL | | let _ = b; -LL | | } -LL | | } - | |_____^ - | -help: collapse nested if block - | -LL ~ if Some(3) == Some(4).map(|x| x - 1) -LL ~ && let Some(b) = Some(4) { -LL | let _ = b; -LL ~ } - | - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs index 55ef55844957..71b82040ff62 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.rs +++ b/src/tools/clippy/tests/ui/collapsible_match.rs @@ -1,5 +1,6 @@ #![warn(clippy::collapsible_match)] #![allow( + clippy::collapsible_if, clippy::equatable_if_let, clippy::needless_return, clippy::no_effect, diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr index 5294a9d6975d..c290d84ec297 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:14:20 + --> tests/ui/collapsible_match.rs:15:20 | LL | Ok(val) => match val { | ____________________^ @@ -10,7 +10,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:14:12 + --> tests/ui/collapsible_match.rs:15:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -21,7 +21,7 @@ LL | Some(n) => foo(n), = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:24:20 + --> tests/ui/collapsible_match.rs:25:20 | LL | Ok(val) => match val { | ____________________^ @@ -32,7 +32,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:24:12 + --> tests/ui/collapsible_match.rs:25:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -41,7 +41,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:34:9 + --> tests/ui/collapsible_match.rs:35:9 | LL | / if let Some(n) = val { LL | | @@ -51,7 +51,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:33:15 + --> tests/ui/collapsible_match.rs:34:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -59,7 +59,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:43:9 + --> tests/ui/collapsible_match.rs:44:9 | LL | / if let Some(n) = val { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:42:15 + --> tests/ui/collapsible_match.rs:43:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -79,7 +79,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:56:9 + --> tests/ui/collapsible_match.rs:57:9 | LL | / match val { LL | | @@ -89,7 +89,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:55:15 + --> tests/ui/collapsible_match.rs:56:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -98,7 +98,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:66:13 + --> tests/ui/collapsible_match.rs:67:13 | LL | / if let Some(n) = val { LL | | @@ -108,7 +108,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:65:12 + --> tests/ui/collapsible_match.rs:66:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -116,7 +116,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:77:9 + --> tests/ui/collapsible_match.rs:78:9 | LL | / match val { LL | | @@ -126,7 +126,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:76:15 + --> tests/ui/collapsible_match.rs:77:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -135,7 +135,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:89:13 + --> tests/ui/collapsible_match.rs:90:13 | LL | / if let Some(n) = val { LL | | @@ -147,7 +147,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:88:12 + --> tests/ui/collapsible_match.rs:89:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -155,7 +155,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:102:20 + --> tests/ui/collapsible_match.rs:103:20 | LL | Ok(val) => match val { | ____________________^ @@ -166,7 +166,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:102:12 + --> tests/ui/collapsible_match.rs:103:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -175,7 +175,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:112:22 + --> tests/ui/collapsible_match.rs:113:22 | LL | Some(val) => match val { | ______________________^ @@ -186,7 +186,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:112:14 + --> tests/ui/collapsible_match.rs:113:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -195,7 +195,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:256:22 + --> tests/ui/collapsible_match.rs:257:22 | LL | Some(val) => match val { | ______________________^ @@ -206,7 +206,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:256:14 + --> tests/ui/collapsible_match.rs:257:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -215,7 +215,7 @@ LL | E::A(val) | E::B(val) => foo(val), | ^^^^^^^^^^^^^^^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:288:9 + --> tests/ui/collapsible_match.rs:289:9 | LL | / if let Some(u) = a { LL | | @@ -225,7 +225,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:287:27 + --> tests/ui/collapsible_match.rs:288:27 | LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding @@ -233,7 +233,7 @@ LL | if let Some(u) = a { | ^^^^^^^ with this pattern, prefixed by `a`: error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:298:9 + --> tests/ui/collapsible_match.rs:299:9 | LL | / if let Some(u) = a { LL | | @@ -243,7 +243,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:297:35 + --> tests/ui/collapsible_match.rs:298:35 | LL | if let Issue9647::A { a: Some(a), .. } = x { | ^ replace this binding diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed index dfbb61683840..7a71829dd62c 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.fixed +++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs index 61cdb2bbe9f8..5d213a09e812 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.rs +++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr index 00a50430a3ee..deb3e9388784 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.stderr +++ b/src/tools/clippy/tests/ui/comparison_to_empty.stderr @@ -1,5 +1,5 @@ error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:8:13 + --> tests/ui/comparison_to_empty.rs:7:13 | LL | let _ = s == ""; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` @@ -8,73 +8,73 @@ LL | let _ = s == ""; = help: to override `-D warnings` add `#[allow(clippy::comparison_to_empty)]` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:10:13 + --> tests/ui/comparison_to_empty.rs:9:13 | LL | let _ = s != ""; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:14:13 + --> tests/ui/comparison_to_empty.rs:13:13 | LL | let _ = v == []; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:16:13 + --> tests/ui/comparison_to_empty.rs:15:13 | LL | let _ = v != []; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:18:8 + --> tests/ui/comparison_to_empty.rs:17:8 | LL | if let [] = &*v {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(*v).is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:21:8 + --> tests/ui/comparison_to_empty.rs:20:8 | LL | if let [] = s {} | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:23:8 + --> tests/ui/comparison_to_empty.rs:22:8 | LL | if let [] = &*s {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:25:8 + --> tests/ui/comparison_to_empty.rs:24:8 | LL | if let [] = &*s | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:27:12 + --> tests/ui/comparison_to_empty.rs:26:12 | LL | && s == [] | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:48:13 + --> tests/ui/comparison_to_empty.rs:47:13 | LL | let _ = s.eq(""); | ^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:50:13 + --> tests/ui/comparison_to_empty.rs:49:13 | LL | let _ = s.ne(""); | ^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:53:13 + --> tests/ui/comparison_to_empty.rs:52:13 | LL | let _ = v.eq(&[]); | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:55:13 + --> tests/ui/comparison_to_empty.rs:54:13 | LL | let _ = v.ne(&[]); | ^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed new file mode 100644 index 000000000000..e698b99edd5c --- /dev/null +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast +} diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs new file mode 100644 index 000000000000..ef65c21563d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::max as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min as usize; //~ confusing_method_to_numeric_cast + let _ = u16::max_value as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min_value as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::maximum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::max as usize; //~ confusing_method_to_numeric_cast + let _ = f32::minimum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::min as usize; //~ confusing_method_to_numeric_cast +} diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr new file mode 100644 index 000000000000..ba90df2059af --- /dev/null +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr @@ -0,0 +1,100 @@ +error: casting function pointer `u16::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:5:13 + | +LL | let _ = u16::max as usize; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::confusing-method-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::confusing_method_to_numeric_cast)]` +help: did you mean to use the associated constant? + | +LL - let _ = u16::max as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:6:13 + | +LL | let _ = u16::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `u16::max_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:7:13 + | +LL | let _ = u16::max_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::max_value as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:8:13 + | +LL | let _ = u16::min_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min_value as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `f32::maximum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:10:13 + | +LL | let _ = f32::maximum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::maximum as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:11:13 + | +LL | let _ = f32::max as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::max as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::minimum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:12:13 + | +LL | let _ = f32::minimum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::minimum as usize; +LL + let _ = f32::MIN as usize; + | + +error: casting function pointer `f32::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:13:13 + | +LL | let _ = f32::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::min as usize; +LL + let _ = f32::MIN as usize; + | + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.fixed b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.fixed new file mode 100644 index 000000000000..9c85c4b84648 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.fixed @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + const fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.rs b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.rs new file mode 100644 index 000000000000..6519be61256e --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.rs @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.stderr b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.stderr new file mode 100644 index 000000000000..a407376d0b9d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.stderr @@ -0,0 +1,17 @@ +error: this could be a `const fn` + --> tests/ui/crashes/missing_const_for_fn_14774.rs:6:5 + | +LL | / fn foo(a: usize) -> usize { +LL | | a + 10 +LL | | } + | |_____^ + | + = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]` +help: make the function `const` + | +LL | const fn foo(a: usize) -> usize { + | +++++ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/enum_variants.rs b/src/tools/clippy/tests/ui/enum_variants.rs index f7bbf83654f1..18c80c7aba43 100644 --- a/src/tools/clippy/tests/ui/enum_variants.rs +++ b/src/tools/clippy/tests/ui/enum_variants.rs @@ -220,4 +220,19 @@ mod issue11494 { } } +mod encapsulated { + mod types { + pub struct FooError; + pub struct BarError; + pub struct BazError; + } + + enum Error { + FooError(types::FooError), + BarError(types::BarError), + BazError(types::BazError), + Other, + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index e19aa4acb4c1..050cdfcba966 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 290393568554..91429bfea276 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), diff --git a/src/tools/clippy/tests/ui/integer_division.rs b/src/tools/clippy/tests/ui/integer_division.rs index 632fedc9e8fe..a40956e2672e 100644 --- a/src/tools/clippy/tests/ui/integer_division.rs +++ b/src/tools/clippy/tests/ui/integer_division.rs @@ -1,5 +1,9 @@ #![warn(clippy::integer_division)] +use std::num::NonZeroU32; + +const TWO: NonZeroU32 = NonZeroU32::new(2).unwrap(); + fn main() { let two = 2; let n = 1 / 2; @@ -12,4 +16,8 @@ fn main() { //~^ integer_division let x = 1. / 2.0; + + let a = 1; + let s = a / TWO; + //~^ integer_division } diff --git a/src/tools/clippy/tests/ui/integer_division.stderr b/src/tools/clippy/tests/ui/integer_division.stderr index 0fe2021a1a9c..c0e34a562f65 100644 --- a/src/tools/clippy/tests/ui/integer_division.stderr +++ b/src/tools/clippy/tests/ui/integer_division.stderr @@ -1,5 +1,5 @@ error: integer division - --> tests/ui/integer_division.rs:5:13 + --> tests/ui/integer_division.rs:9:13 | LL | let n = 1 / 2; | ^^^^^ @@ -9,7 +9,7 @@ LL | let n = 1 / 2; = help: to override `-D warnings` add `#[allow(clippy::integer_division)]` error: integer division - --> tests/ui/integer_division.rs:8:13 + --> tests/ui/integer_division.rs:12:13 | LL | let o = 1 / two; | ^^^^^^^ @@ -17,12 +17,20 @@ LL | let o = 1 / two; = help: division of integers may cause loss of precision. consider using floats error: integer division - --> tests/ui/integer_division.rs:11:13 + --> tests/ui/integer_division.rs:15:13 | LL | let p = two / 4; | ^^^^^^^ | = help: division of integers may cause loss of precision. consider using floats -error: aborting due to 3 previous errors +error: integer division + --> tests/ui/integer_division.rs:21:13 + | +LL | let s = a / TWO; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.rs b/src/tools/clippy/tests/ui/let_underscore_untyped.rs index 26ba8682dc2d..7f74ab035bda 100644 --- a/src/tools/clippy/tests/ui/let_underscore_untyped.rs +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.rs @@ -6,7 +6,6 @@ extern crate proc_macros; use proc_macros::with_span; -use clippy_utils::is_from_proc_macro; use std::boxed::Box; use std::fmt::Display; use std::future::Future; diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.stderr b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr index 86cdd5c662cc..54b955ac3a56 100644 --- a/src/tools/clippy/tests/ui/let_underscore_untyped.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr @@ -1,11 +1,11 @@ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:51:5 + --> tests/ui/let_underscore_untyped.rs:50:5 | LL | let _ = a(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:51:10 + --> tests/ui/let_underscore_untyped.rs:50:10 | LL | let _ = a(); | ^ @@ -13,49 +13,49 @@ LL | let _ = a(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_untyped)]` error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:53:5 + --> tests/ui/let_underscore_untyped.rs:52:5 | LL | let _ = b(1); | ^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:53:10 + --> tests/ui/let_underscore_untyped.rs:52:10 | LL | let _ = b(1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:56:5 + --> tests/ui/let_underscore_untyped.rs:55:5 | LL | let _ = d(&1); | ^^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:56:10 + --> tests/ui/let_underscore_untyped.rs:55:10 | LL | let _ = d(&1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:58:5 + --> tests/ui/let_underscore_untyped.rs:57:5 | LL | let _ = e(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:58:10 + --> tests/ui/let_underscore_untyped.rs:57:10 | LL | let _ = e(); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:60:5 + --> tests/ui/let_underscore_untyped.rs:59:5 | LL | let _ = f(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:60:10 + --> tests/ui/let_underscore_untyped.rs:59:10 | LL | let _ = f(); | ^ diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.fixed b/src/tools/clippy/tests/ui/let_with_type_underscore.fixed new file mode 100644 index 000000000000..7a4af4e3d1e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_with_type_underscore.fixed @@ -0,0 +1,47 @@ +//@aux-build: proc_macros.rs +#![allow(unused)] +#![warn(clippy::let_with_type_underscore)] +#![allow(clippy::let_unit_value, clippy::needless_late_init)] + +extern crate proc_macros; + +fn func() -> &'static str { + "" +} + +#[rustfmt::skip] +fn main() { + // Will lint + let x = 1; + //~^ let_with_type_underscore + let _ = 2; + //~^ let_with_type_underscore + let x = func(); + //~^ let_with_type_underscore + let x; + //~^ let_with_type_underscore + x = (); + + let x = 1; // Will not lint, Rust infers this to an integer before Clippy + let x = func(); + let x: Vec<_> = Vec::::new(); + let x: [_; 1] = [1]; + let x = 1; + //~^ let_with_type_underscore + + // Do not lint from procedural macros + proc_macros::with_span! { + span + let x: _ = (); + // Late initialization + let x: _; + x = (); + // Ensure weird formatting will not break it (hopefully) + let x : _ = 1; + let x +: _ = 1; + let x : + _; + x = (); + }; +} diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr index 2284d1fe2e48..9179f9922071 100644 --- a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr +++ b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr @@ -4,13 +4,13 @@ error: variable declared with type underscore LL | let x: _ = 1; | ^^^^^^^^^^^^^ | -help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:15:10 - | -LL | let x: _ = 1; - | ^^^ = note: `-D clippy::let-with-type-underscore` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_with_type_underscore)]` +help: remove the explicit type `_` declaration + | +LL - let x: _ = 1; +LL + let x = 1; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:17:5 @@ -19,10 +19,10 @@ LL | let _: _ = 2; | ^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:17:10 | -LL | let _: _ = 2; - | ^^^ +LL - let _: _ = 2; +LL + let _ = 2; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:19:5 @@ -31,10 +31,10 @@ LL | let x: _ = func(); | ^^^^^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:19:10 | -LL | let x: _ = func(); - | ^^^ +LL - let x: _ = func(); +LL + let x = func(); + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:21:5 @@ -43,10 +43,10 @@ LL | let x: _; | ^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:21:10 | -LL | let x: _; - | ^^^ +LL - let x: _; +LL + let x; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:29:5 @@ -55,10 +55,10 @@ LL | let x : _ = 1; | ^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:29:10 | -LL | let x : _ = 1; - | ^^^^ +LL - let x : _ = 1; +LL + let x = 1; + | error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index a753566b34cb..3781ba1676f5 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -514,3 +514,35 @@ mod issue13768 { }; } } + +mod issue14598 { + fn bar() -> Result { + let value = match foo() { + //~^ manual_let_else + Err(_) => return Err("abc"), + Ok(value) => value, + }; + + let w = Some(0); + let v = match w { + //~^ manual_let_else + None => return Err("abc"), + Some(x) => x, + }; + + enum Foo { + Foo(T), + } + + let v = match Foo::Foo(Some(())) { + Foo::Foo(Some(_)) => return Err("abc"), + Foo::Foo(v) => v, + }; + + Ok(value == 42) + } + + fn foo() -> Result { + todo!() + } +} diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index ef0421921141..a1eea0419291 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else.stderr @@ -529,5 +529,25 @@ LL + return; LL + }; | -error: aborting due to 33 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:520:9 + | +LL | / let value = match foo() { +LL | | +LL | | Err(_) => return Err("abc"), +LL | | Ok(value) => value, +LL | | }; + | |__________^ help: consider writing: `let Ok(value) = foo() else { return Err("abc") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:527:9 + | +LL | / let v = match w { +LL | | +LL | | None => return Err("abc"), +LL | | Some(x) => x, +LL | | }; + | |__________^ help: consider writing: `let Some(v) = w else { return Err("abc") };` + +error: aborting due to 35 previous errors diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed index 3f73d6e5a1a6..304be05f6c4c 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.saturating_add(1); //~^ manual_saturating_arithmetic diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs index 98246a5cd96c..c2b570e974ac 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); //~^ manual_saturating_arithmetic diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr index 9d133d8a073b..2f006a3ae170 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr @@ -1,5 +1,5 @@ error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:6:13 + --> tests/ui/manual_saturating_arithmetic.rs:4:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` @@ -8,19 +8,19 @@ LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); = help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:8:13 + --> tests/ui/manual_saturating_arithmetic.rs:6:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:10:13 + --> tests/ui/manual_saturating_arithmetic.rs:8:13 | LL | let _ = 1u8.checked_add(1).unwrap_or(255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:12:13 + --> tests/ui/manual_saturating_arithmetic.rs:10:13 | LL | let _ = 1u128 | _____________^ @@ -30,49 +30,49 @@ LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:18:13 + --> tests/ui/manual_saturating_arithmetic.rs:16:13 | LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:21:13 + --> tests/ui/manual_saturating_arithmetic.rs:19:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:23:13 + --> tests/ui/manual_saturating_arithmetic.rs:21:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:25:13 + --> tests/ui/manual_saturating_arithmetic.rs:23:13 | LL | let _ = 1u8.checked_sub(1).unwrap_or(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:30:13 + --> tests/ui/manual_saturating_arithmetic.rs:28:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:32:13 + --> tests/ui/manual_saturating_arithmetic.rs:30:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:34:13 + --> tests/ui/manual_saturating_arithmetic.rs:32:13 | LL | let _ = 1i8.checked_add(1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:36:13 + --> tests/ui/manual_saturating_arithmetic.rs:34:13 | LL | let _ = 1i128 | _____________^ @@ -82,25 +82,25 @@ LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:40:13 + --> tests/ui/manual_saturating_arithmetic.rs:38:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:42:13 + --> tests/ui/manual_saturating_arithmetic.rs:40:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:44:13 + --> tests/ui/manual_saturating_arithmetic.rs:42:13 | LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:46:13 + --> tests/ui/manual_saturating_arithmetic.rs:44:13 | LL | let _ = 1i128 | _____________^ @@ -110,25 +110,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:54:13 + --> tests/ui/manual_saturating_arithmetic.rs:52:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:56:13 + --> tests/ui/manual_saturating_arithmetic.rs:54:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:58:13 + --> tests/ui/manual_saturating_arithmetic.rs:56:13 | LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:60:13 + --> tests/ui/manual_saturating_arithmetic.rs:58:13 | LL | let _ = 1i128 | _____________^ @@ -138,25 +138,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:64:13 + --> tests/ui/manual_saturating_arithmetic.rs:62:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:66:13 + --> tests/ui/manual_saturating_arithmetic.rs:64:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:68:13 + --> tests/ui/manual_saturating_arithmetic.rs:66:13 | LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:70:13 + --> tests/ui/manual_saturating_arithmetic.rs:68:13 | LL | let _ = 1i128 | _____________^ diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.fixed b/src/tools/clippy/tests/ui/manual_slice_fill.fixed index bba863247f5d..d07d1d60e2c1 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_fill.fixed @@ -123,3 +123,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList(T); + + impl ZipList { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl Index for ZipList { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl IndexMut for ZipList { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.rs b/src/tools/clippy/tests/ui/manual_slice_fill.rs index 44c60dc40f07..c74ab2225c0a 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.rs +++ b/src/tools/clippy/tests/ui/manual_slice_fill.rs @@ -136,3 +136,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList(T); + + impl ZipList { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl Index for ZipList { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl IndexMut for ZipList { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index 9dae9fcae079..41ca44ceef4e 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -106,3 +106,16 @@ fn issue_12928() { fn allowed_manual_unwrap_or_zero() -> u32 { Some(42).unwrap_or_default() } + +mod issue14716 { + struct Foo { + name: Option, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index 539d7a8bbae5..343fbc4879ce 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -147,3 +147,16 @@ fn allowed_manual_unwrap_or_zero() -> u32 { 0 } } + +mod issue14716 { + struct Foo { + name: Option, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_if.fixed index 347dbff7c595..c839156bed9b 100644 --- a/src/tools/clippy/tests/ui/needless_if.fixed +++ b/src/tools/clippy/tests/ui/needless_if.fixed @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_if.rs index 5e0f2a14408b..11103af5c559 100644 --- a/src/tools/clippy/tests/ui/needless_if.rs +++ b/src/tools/clippy/tests/ui/needless_if.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, diff --git a/src/tools/clippy/tests/ui/needless_if.stderr b/src/tools/clippy/tests/ui/needless_if.stderr index 62cdf2459448..4b56843bd522 100644 --- a/src/tools/clippy/tests/ui/needless_if.stderr +++ b/src/tools/clippy/tests/ui/needless_if.stderr @@ -1,5 +1,5 @@ error: this `if` branch is empty - --> tests/ui/needless_if.rs:27:5 + --> tests/ui/needless_if.rs:26:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it @@ -8,13 +8,13 @@ LL | if (true) {} = help: to override `-D warnings` add `#[allow(clippy::needless_if)]` error: this `if` branch is empty - --> tests/ui/needless_if.rs:30:5 + --> tests/ui/needless_if.rs:29:5 | LL | if maybe_side_effect() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();` error: this `if` branch is empty - --> tests/ui/needless_if.rs:36:5 + --> tests/ui/needless_if.rs:35:5 | LL | / if { LL | | @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:51:5 + --> tests/ui/needless_if.rs:50:5 | LL | / if { LL | | @@ -57,19 +57,19 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:96:5 + --> tests/ui/needless_if.rs:95:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_if.rs:99:5 + --> tests/ui/needless_if.rs:98:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:104:5 + --> tests/ui/needless_if.rs:103:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index f832752ccd79..b686a8e9f1a0 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index a52fbf529234..23772ff70293 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index b24c12758816..e3e25cdc8d76 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> tests/ui/needless_late_init.rs:27:5 + --> tests/ui/needless_late_init.rs:25:5 | LL | let a; | ^^^^^^ created here @@ -17,7 +17,7 @@ LL ~ let a = "zero"; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:31:5 + --> tests/ui/needless_late_init.rs:29:5 | LL | let b; | ^^^^^^ created here @@ -35,7 +35,7 @@ LL ~ let b = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:33:5 + --> tests/ui/needless_late_init.rs:31:5 | LL | let c; | ^^^^^^ created here @@ -52,7 +52,7 @@ LL ~ let c = 2; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:38:5 + --> tests/ui/needless_late_init.rs:36:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -68,7 +68,7 @@ LL ~ let d: usize = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:42:5 + --> tests/ui/needless_late_init.rs:40:5 | LL | let e; | ^^^^^^ created here @@ -84,7 +84,7 @@ LL ~ let e = format!("{}", d); | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:48:5 + --> tests/ui/needless_late_init.rs:46:5 | LL | let a; | ^^^^^^ @@ -103,7 +103,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:58:5 + --> tests/ui/needless_late_init.rs:56:5 | LL | let b; | ^^^^^^ @@ -120,7 +120,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:66:5 + --> tests/ui/needless_late_init.rs:64:5 | LL | let d; | ^^^^^^ @@ -138,7 +138,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:75:5 + --> tests/ui/needless_late_init.rs:73:5 | LL | let e; | ^^^^^^ @@ -155,7 +155,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:83:5 + --> tests/ui/needless_late_init.rs:81:5 | LL | let f; | ^^^^^^ @@ -169,7 +169,7 @@ LL ~ 1 => "three", | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:90:5 + --> tests/ui/needless_late_init.rs:88:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -186,7 +186,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:99:5 + --> tests/ui/needless_late_init.rs:97:5 | LL | let x; | ^^^^^^ created here @@ -203,7 +203,7 @@ LL ~ let x = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:104:5 + --> tests/ui/needless_late_init.rs:102:5 | LL | let x; | ^^^^^^ created here @@ -220,7 +220,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:109:5 + --> tests/ui/needless_late_init.rs:107:5 | LL | let x; | ^^^^^^ created here @@ -238,7 +238,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:129:5 + --> tests/ui/needless_late_init.rs:127:5 | LL | let a; | ^^^^^^ @@ -257,7 +257,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:147:5 + --> tests/ui/needless_late_init.rs:145:5 | LL | let a; | ^^^^^^ @@ -276,7 +276,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:300:5 + --> tests/ui/needless_late_init.rs:298:5 | LL | let r; | ^^^^^^ created here diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed index 2b30c8f984eb..d6c35d8097c5 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs index c52338eee83c..996ef050d691 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index 33a5308bd357..dc9d6491691f 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -1,4 +1,4 @@ -#![feature(let_chains, if_let_guard)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index 60bce2994ea3..2e9714ad8e75 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -1,4 +1,4 @@ -#![feature(let_chains, if_let_guard)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index acf7914d2536..55e287b91596 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -63,6 +63,7 @@ #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] #![allow(clippy::reversed_empty_ranges)] +#![allow(unnecessary_transmutes)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -132,5 +133,9 @@ #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` #![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 32641a684a44..31dcd2cea081 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -63,6 +63,7 @@ #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] #![allow(clippy::reversed_empty_ranges)] +#![allow(unnecessary_transmutes)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -132,5 +133,9 @@ #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` #![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` +#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index e9d2debff91a..a8d5c96acc3a 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,412 +8,436 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: aborting due to 69 previous errors +error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:136:9 + | +LL | #![warn(clippy::transmute_int_to_float)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:137:9 + | +LL | #![warn(clippy::transmute_int_to_char)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:138:9 + | +LL | #![warn(clippy::transmute_float_to_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:139:9 + | +LL | #![warn(clippy::transmute_num_to_bytes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: aborting due to 73 previous errors diff --git a/src/tools/clippy/tests/ui/return_and_then.fixed b/src/tools/clippy/tests/ui/return_and_then.fixed index 74efa14eeec8..8d9481d15951 100644 --- a/src/tools/clippy/tests/ui/return_and_then.fixed +++ b/src/tools/clippy/tests/ui/return_and_then.fixed @@ -67,8 +67,60 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option { + if shortcut { + return { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + }; + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option { + if shortcut { + return { + let mut x = Some("")?; + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }; + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/src/tools/clippy/tests/ui/return_and_then.rs b/src/tools/clippy/tests/ui/return_and_then.rs index 188dc57e588c..beada921a918 100644 --- a/src/tools/clippy/tests/ui/return_and_then.rs +++ b/src/tools/clippy/tests/ui/return_and_then.rs @@ -63,8 +63,55 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option { + if shortcut { + return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option { + if shortcut { + return Some("").and_then(|mut x| { + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }); + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr index a7acbe7b3401..5feca8828605 100644 --- a/src/tools/clippy/tests/ui/return_and_then.stderr +++ b/src/tools/clippy/tests/ui/return_and_then.stderr @@ -101,5 +101,50 @@ LL + })?; LL + if x.len() > 2 { Some(3) } else { None } | -error: aborting due to 7 previous errors +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:69:13 + | +LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:77:20 + | +LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ return { +LL + let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:85:20 + | +LL | return Some("").and_then(|mut x| { + | ____________________^ +LL | | let x = format!("{x}."); +LL | | if x.len() > 2 { Some(3) } else { None } +LL | | }); + | |______________^ + | +help: try + | +LL ~ return { +LL + let mut x = Some("")?; +LL + let x = format!("{x}."); +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.fixed b/src/tools/clippy/tests/ui/to_digit_is_some.fixed index 627d54c5f738..ff6b32e6bd18 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.fixed +++ b/src/tools/clippy/tests/ui/to_digit_is_some.fixed @@ -9,3 +9,20 @@ fn main() { let _ = char::is_digit(c, 8); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some +} diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.rs b/src/tools/clippy/tests/ui/to_digit_is_some.rs index d4eccc9931f1..5ba086174331 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.rs +++ b/src/tools/clippy/tests/ui/to_digit_is_some.rs @@ -9,3 +9,20 @@ fn main() { let _ = char::to_digit(c, 8).is_some(); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some +} diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.stderr b/src/tools/clippy/tests/ui/to_digit_is_some.stderr index f41382a60d53..5ffedb4683f1 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.stderr +++ b/src/tools/clippy/tests/ui/to_digit_is_some.stderr @@ -13,5 +13,17 @@ error: use of `.to_digit(..).is_some()` LL | let _ = char::to_digit(c, 8).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `char::is_digit(c, 8)` -error: aborting due to 2 previous errors +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:16:9 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:26:5 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 2b8b6c539ad3..e968e7a59244 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -116,138 +116,6 @@ fn int_to_bool() { //~^ transmute_int_to_bool } -#[warn(clippy::transmute_int_to_float)] -mod int_to_float { - fn test() { - let _: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - let _: f16 = unsafe { std::mem::transmute(0_i16) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_u64) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_u128) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - } - - mod issue_5747 { - const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - - const fn from_bits_16(v: i16) -> f16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_32(v: i32) -> f32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_64(v: u64) -> f64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_128(v: u128) -> f128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - } -} - -mod num_to_bytes { - fn test() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } - const fn test_const() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } -} - fn bytes_to_str(mb: &mut [u8]) { const B: &[u8] = b""; diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 1bb70151965c..79528ec06f1b 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -97,230 +97,8 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:122:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - | - = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:125:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_i16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_i16 as u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:128:31 - | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:131:31 - | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` - -error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:134:31 - | -LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` - -error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:137:31 - | -LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:140:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_u128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_u128)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:143:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:148:39 - | -LL | const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:151:39 - | -LL | const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:154:39 - | -LL | const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:157:41 - | -LL | const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:161:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(v as u16)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:166:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(v as u32)` - -error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:171:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(v)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:176:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(v)` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:185:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - | - = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:188:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:191:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:194:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:197:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:200:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:203:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:206:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:209:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:212:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:218:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:221:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:224:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:227:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:230:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:233:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:236:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:239:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:242:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:245:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:254:28 + --> tests/ui/transmute.rs:122:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -329,16 +107,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:257:32 + --> tests/ui/transmute.rs:125:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:260:30 + --> tests/ui/transmute.rs:128:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 54 previous errors +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed deleted file mode 100644 index 844445907d7c..000000000000 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { 1f32.to_bits() }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { 1f64.to_bits() as i64 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1.0f64.to_bits() }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { (-1.0f64).to_bits() }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { 1f16.to_bits() as i16 }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { 1f128.to_bits() }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { v.to_bits() as i64 } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { v.to_bits() as i128 } - //~^ transmute_float_to_int - } -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs deleted file mode 100644 index a1f3b15bbfee..000000000000 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1.0) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(-1.0) }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr deleted file mode 100644 index 223cbc4e90c0..000000000000 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:7:27 - | -LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` - | - = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_float_to_int)]` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:10:27 - | -LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:13:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:16:27 - | -LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:19:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:22:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` - -error: transmute from a `f16` to a `i16` - --> tests/ui/transmute_float_to_int.rs:27:35 - | -LL | const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f16.to_bits() as i16` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:30:35 - | -LL | const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:33:35 - | -LL | const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f128` to a `u128` - --> tests/ui/transmute_float_to_int.rs:36:37 - | -LL | const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f128.to_bits()` - -error: transmute from a `f16` to a `u16` - --> tests/ui/transmute_float_to_int.rs:40:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:45:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:50:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i64` - -error: transmute from a `f128` to a `i128` - --> tests/ui/transmute_float_to_int.rs:55:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i128` - -error: aborting due to 14 previous errors - diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed deleted file mode 100644 index 28644aa9ebbb..000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -fn int_to_char() { - let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.rs b/src/tools/clippy/tests/ui/transmute_int_to_char.rs deleted file mode 100644 index 8c83ecc8914b..000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -fn int_to_char() { - let _: char = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char.stderr deleted file mode 100644 index e3a3620f28b7..000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char.rs:5:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char.rs:8:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed deleted file mode 100644 index e6e09a2be4bf..000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs deleted file mode 100644 index 0f2106df00e6..000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr deleted file mode 100644 index d94580a84d7a..000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:17:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:20:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index d325887bfba3..e75678d5fd93 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -12,7 +12,7 @@ pub fn foo(_t: T) where T: Copy, T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { unimplemented!(); } @@ -30,7 +30,7 @@ trait LintBounds where Self: Clone, Self: Copy + Default + Ord, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds Self: Add + AddAssign + Sub + SubAssign, Self: Mul + MulAssign + Div + DivAssign, { @@ -105,13 +105,13 @@ where pub fn f() where T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } pub fn g() where T: ?Sized, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } @@ -137,10 +137,27 @@ mod issue8772_pass { pub fn f(arg: usize) where T: Trait, Box<[String]>, bool> + 'static, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds U: Clone + Sync + 'static, { } } +struct Issue14744<'a, K: 'a> +where + K: Clone, +{ + phantom: std::marker::PhantomData<&'a K>, +} +//~^^^^ type_repetition_in_bounds + +struct ComplexType +where + Vec: Clone, + Vec: Clone, +{ + t: T, +} +//~^^^^ type_repetition_in_bounds + fn main() {} diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr index 77944c950457..de1b14da1985 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:14:5 | LL | T: Clone, @@ -11,7 +11,7 @@ note: the lint level is defined here LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this type has already been used as a bound predicate +error: type `Self` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:32:5 | LL | Self: Copy + Default + Ord, @@ -19,7 +19,7 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:107:5 | LL | T: Clone, @@ -27,7 +27,7 @@ LL | T: Clone, | = help: consider combining the bounds: `T: ?Sized + Clone` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:113:5 | LL | T: ?Sized, @@ -35,13 +35,29 @@ LL | T: ?Sized, | = help: consider combining the bounds: `T: Clone + ?Sized` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:139:9 | LL | T: Trait, Box<[String]>, bool> + 'static, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider combining the bounds: `T: ?Sized + Trait, Box<[String]>, bool>` + = help: consider combining the bounds: `T: ?Sized + Trait, Box<[String]>, bool> + 'static` -error: aborting due to 5 previous errors +error: type `K` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:148:5 + | +LL | K: Clone, + | ^^^^^^^^ + | + = help: consider combining the bounds: `K: 'a + Clone` + +error: type `Vec` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:157:5 + | +LL | Vec: Clone, + | ^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `Vec: Clone + Clone` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs index 5aaf7b9f5b59..433459253dd7 100644 --- a/src/tools/clippy/tests/ui/unused_async.rs +++ b/src/tools/clippy/tests/ui/unused_async.rs @@ -119,3 +119,11 @@ fn main() { foo(); bar(); } + +mod issue14704 { + use std::sync::Arc; + + trait Action { + async fn cancel(self: Arc) {} + } +} diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.rs b/src/tools/clippy/tests/ui/unwrap_expect_used.rs index d0bb571273b5..b429f3a8a0bb 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.rs +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.rs @@ -66,3 +66,20 @@ fn main() { SOME.expect("Still not three?"); } } + +mod with_expansion { + macro_rules! open { + ($file:expr) => { + std::fs::File::open($file) + }; + } + + fn test(file: &str) { + use std::io::Read; + let mut s = String::new(); + let _ = open!(file).unwrap(); //~ unwrap_used + let _ = open!(file).expect("can open"); //~ expect_used + let _ = open!(file).unwrap_err(); //~ unwrap_used + let _ = open!(file).expect_err("can open"); //~ expect_used + } +} diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr index 79eac3f58ccb..6fd1b84d8123 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr @@ -50,5 +50,37 @@ LL | a.expect_err("Hello error!"); | = note: if this value is an `Ok`, it will panic -error: aborting due to 6 previous errors +error: used `unwrap()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:80:17 + | +LL | let _ = open!(file).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `expect()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:81:17 + | +LL | let _ = open!(file).expect("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `unwrap_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:82:17 + | +LL | let _ = open!(file).unwrap_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: used `expect_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:83:17 + | +LL | let _ = open!(file).expect_err("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index f27b109e9953..eb2f9f9dd61f 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -13,7 +13,9 @@ allow-unauthenticated = [ [note] -[canonicalize-issue-links] +[close] + +[issue-links] # Prevents mentions in commits to avoid users being spammed [no-mentions] @@ -42,7 +44,7 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", - "samueltardieu", + "Manishearth", ] [assign.owners] diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 93f7b1cb7cf2..3b544d8b8281 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -37,7 +37,7 @@ libc = "0.2" miow = "0.6" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 3c87bcb205fc..75f24adb70fa 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2609,18 +2609,19 @@ impl<'test> TestCx<'test> { (expected, actual) }; - // Write the actual output to a file in build/ - let test_name = self.config.compare_mode.as_ref().map_or("", |m| m.to_str()); + // Write the actual output to a file in build directory. let actual_path = self .output_base_name() .with_extra_extension(self.revision.unwrap_or("")) - .with_extra_extension(test_name) + .with_extra_extension( + self.config.compare_mode.as_ref().map(|cm| cm.to_str()).unwrap_or(""), + ) .with_extra_extension(stream); if let Err(err) = fs::write(&actual_path, &actual) { - self.fatal(&format!("failed to write {stream} to `{actual_path:?}`: {err}",)); + self.fatal(&format!("failed to write {stream} to `{actual_path}`: {err}",)); } - println!("Saved the actual {stream} to {actual_path:?}"); + println!("Saved the actual {stream} to `{actual_path}`"); if !self.config.bless { if expected.is_empty() { @@ -2646,13 +2647,16 @@ impl<'test> TestCx<'test> { if !actual.is_empty() { if let Err(err) = fs::write(&expected_path, &actual) { - self.fatal(&format!("failed to write {stream} to `{expected_path:?}`: {err}")); + self.fatal(&format!("failed to write {stream} to `{expected_path}`: {err}")); } - println!("Blessing the {stream} of {test_name} in {expected_path:?}"); + println!( + "Blessing the {stream} of `{test_name}` as `{expected_path}`", + test_name = self.testpaths.file + ); } } - println!("\nThe actual {0} differed from the expected {0}.", stream); + println!("\nThe actual {stream} differed from the expected {stream}"); if self.config.bless { CompareOutcome::Blessed } else { CompareOutcome::Differed } } diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 7b7be97aa518..e4d7abdb0f73 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -6,7 +6,7 @@ name = "miri" repository = "https://github.com/rust-lang/miri" version = "0.1.0" default-run = "miri" -edition = "2021" +edition = "2024" [lib] test = true # we have unit tests diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index ed142b0e2114..23048914af18 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -5,7 +5,7 @@ license = "MIT OR Apache-2.0" name = "cargo-miri" repository = "https://github.com/rust-lang/miri" version = "0.1.0" -edition = "2021" +edition = "2024" [[bin]] name = "cargo-miri" diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs index 322ef0a6c2aa..4c01a81fdfdc 100644 --- a/src/tools/miri/cargo-miri/src/main.rs +++ b/src/tools/miri/cargo-miri/src/main.rs @@ -63,27 +63,37 @@ fn main() { return; } - // The way rustdoc invokes rustc is indistinguishable from the way cargo invokes rustdoc by the - // arguments alone. `phase_cargo_rustdoc` sets this environment variable to let us disambiguate. - if env::var_os("MIRI_CALLED_FROM_RUSTDOC").is_some() { - // ...however, we then also see this variable when rustdoc invokes us as the testrunner! - // The runner is invoked as `$runtool ($runtool-arg)* output_file`; - // since we don't specify any runtool-args, and rustdoc supplies multiple arguments to - // the test-builder unconditionally, we can just check the number of remaining arguments: - if args.len() == 1 { - phase_runner(args, RunnerPhase::Rustdoc); - } else { - phase_rustc(args, RustcPhase::Rustdoc); - } - - return; - } - let Some(first) = args.next() else { show_error!( "`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`" ) }; + + // The way rustdoc invokes rustc is indistinguishable from the way cargo invokes rustdoc by the + // arguments alone. `phase_cargo_rustdoc` sets this environment variable to let us disambiguate. + if env::var_os("MIRI_CALLED_FROM_RUSTDOC").is_some() { + // ...however, we then also see this variable when rustdoc invokes us as the testrunner! + // In that case the first argument is `runner` and there are no more arguments. + match first.as_str() { + "runner" => phase_runner(args, RunnerPhase::Rustdoc), + flag if flag.starts_with("--") || flag.starts_with("@") => { + // This is probably rustdoc invoking us to build the test. But we need to get `first` + // "back onto the iterator", it is some part of the rustc invocation. + phase_rustc(iter::once(first).chain(args), RustcPhase::Rustdoc); + } + _ => { + show_error!( + "`cargo-miri` failed to recognize which phase of the build process this is, please report a bug.\n\ + We are inside MIRI_CALLED_FROM_RUSTDOC.\n\ + The command-line arguments were: {:#?}", + Vec::from_iter(env::args()), + ); + } + } + + return; + } + match first.as_str() { "miri" => phase_cargo_miri(args), "runner" => phase_runner(args, RunnerPhase::Cargo), diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index cb62e12413c8..4857f62cd3a1 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -176,6 +176,11 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // Set `--target-dir` to `miri` inside the original target directory. let target_dir = get_target_dir(&metadata); cmd.arg("--target-dir").arg(target_dir); + // Only when running in x.py (where we are running with beta cargo): set `RUSTC_STAGE`. + // Will have to be removed on next bootstrap bump. tag: cfg(bootstrap). + if env::var_os("RUSTC_STAGE").is_some() { + cmd.arg("-Zdoctest-xcompile"); + } // *After* we set all the flags that need setting, forward everything else. Make sure to skip // `--target-dir` (which would otherwise be set twice). @@ -666,11 +671,6 @@ pub fn phase_rustdoc(mut args: impl Iterator) { if arg == "--extern" { // Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files. forward_patched_extern_arg(&mut args, &mut cmd); - } else if arg == "--test-runtool" { - // An existing --test-runtool flag indicates cargo is running in cross-target mode, which we don't support. - // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag; - // otherwise, we won't be called as rustdoc at all. - show_error!("cross-interpreting doctests is not currently supported by Miri."); } else { cmd.arg(arg); } @@ -702,10 +702,10 @@ pub fn phase_rustdoc(mut args: impl Iterator) { // make sure the 'miri' flag is set for rustdoc cmd.arg("--cfg").arg("miri"); - // Make rustdoc call us back. + // Make rustdoc call us back for the build. + // (cargo already sets `--test-runtool` to us since we are the cargo test runner.) let cargo_miri_path = env::current_exe().expect("current executable path invalid"); cmd.arg("--test-builder").arg(&cargo_miri_path); // invoked by forwarding most arguments - cmd.arg("--test-runtool").arg(&cargo_miri_path); // invoked with just a single path argument debug_cmd("[cargo-miri rustdoc]", verbose, &cmd); exec(cmd) diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index 7afc8481009c..b9b58c04f9e4 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -24,11 +24,9 @@ pub fn setup( let ask_user = !only_setup; let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path let show_setup = only_setup && !print_sysroot; - if !only_setup { - if let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") { - // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`. - return sysroot.into(); - } + if !only_setup && let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") { + // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`. + return sysroot.into(); } // Determine where the rust sources are located. The env var trumps auto-detection. diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml index a04898de6aba..462a9cc62bff 100644 --- a/src/tools/miri/miri-script/Cargo.toml +++ b/src/tools/miri/miri-script/Cargo.toml @@ -6,7 +6,7 @@ name = "miri-script" repository = "https://github.com/rust-lang/miri" version = "0.1.0" default-run = "miri-script" -edition = "2021" +edition = "2024" [workspace] # We make this a workspace root so that cargo does not go looking in ../Cargo.toml for the workspace root. diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 1c9750e2cbdc..3b7b159aeab7 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -675,11 +675,9 @@ impl Command { let mut early_flags = Vec::::new(); // In `dep` mode, the target is already passed via `MIRI_TEST_TARGET` - if !dep { - if let Some(target) = &target { - early_flags.push("--target".into()); - early_flags.push(target.into()); - } + if !dep && let Some(target) = &target { + early_flags.push("--target".into()); + early_flags.push(target.into()); } early_flags.push("--edition".into()); early_flags.push(edition.as_deref().unwrap_or("2021").into()); @@ -707,10 +705,8 @@ impl Command { // Add Miri flags let mut cmd = cmd.args(&miri_flags).args(&early_flags).args(&flags); // For `--dep` we also need to set the target in the env var. - if dep { - if let Some(target) = &target { - cmd = cmd.env("MIRI_TEST_TARGET", target); - } + if dep && let Some(target) = &target { + cmd = cmd.env("MIRI_TEST_TARGET", target); } // Finally, run the thing. Ok(cmd.run()?) diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs index c039b4827ee3..5c2a055990fd 100644 --- a/src/tools/miri/miri-script/src/util.rs +++ b/src/tools/miri/miri-script/src/util.rs @@ -213,7 +213,7 @@ impl MiriEnv { let toolchain = &self.toolchain; let mut cmd = cmd!( self.sh, - "rustfmt +{toolchain} --edition=2021 --config-path {config_path} --unstable-features --skip-children {flags...}" + "rustfmt +{toolchain} --edition=2024 --config-path {config_path} --unstable-features --skip-children {flags...}" ); if first { // Log an abbreviating command, and only once. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 97bc826b57a4..8b98fe3c4fc3 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -2ad5f8607d0e192b60b130e5cc416b477b351c18 +ac17c3486c6fdfbb0c3c18b99f3d8dfbff625d29 diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index f249b58aeb66..469fc2649703 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -459,7 +459,7 @@ fn jemalloc_magic() { // linking, so we need to explicitly depend on the function. #[cfg(target_os = "macos")] { - extern "C" { + unsafe extern "C" { fn _rjem_je_zone_register(); } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index b2fd9b2bf054..f5a0013047aa 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -179,7 +179,7 @@ impl NodeDebugInfo { /// Add a name to the tag. If a same tag is associated to several pointers, /// it can have several names which will be separated by commas. pub fn add_name(&mut self, name: &str) { - if let Some(ref mut prev_name) = &mut self.name { + if let Some(prev_name) = &mut self.name { prev_name.push_str(", "); prev_name.push_str(name); } else { diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 89768077d878..10570a37e5d8 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -648,8 +648,7 @@ impl<'tcx> MiriMachine<'tcx> { AccessedAlloc(AllocId(id), access_kind) => format!("{access_kind} to allocation with id {id}"), FreedAlloc(AllocId(id)) => format!("freed allocation with id {id}"), - RejectedIsolatedOp(ref op) => - format!("{op} was made to return an error due to isolation"), + RejectedIsolatedOp(op) => format!("{op} was made to return an error due to isolation"), ProgressReport { .. } => format!("progress report: current operation being executed is here"), Int2Ptr { .. } => format!("integer-to-pointer cast"), diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index a3aa8bbbfb32..8e7c9edfcc07 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -932,12 +932,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) } - /// Check that the ABI is what we expect. - fn check_abi<'a>(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: Conv) -> InterpResult<'a, ()> { + /// Check that the calling convention is what we expect. + fn check_callconv<'a>( + &self, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: Conv, + ) -> InterpResult<'a, ()> { if fn_abi.conv != exp_abi { throw_ub_format!( - "calling a function with ABI {:?} using caller ABI {:?}", - exp_abi, + "calling a function with calling convention {exp_abi} using caller calling convention {}", fn_abi.conv ); } @@ -973,7 +976,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { exp_abi: Conv, link_name: Symbol, ) -> InterpResult<'tcx, ()> { - self.check_abi(abi, exp_abi)?; + self.check_callconv(abi, exp_abi)?; if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? { // If compiler-builtins is providing the symbol, then don't treat it as a clash. // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 58b93ae82a1e..0b7a067058b9 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -9,7 +9,6 @@ #![feature(variant_count)] #![feature(yeet_expr)] #![feature(nonzero_ops)] -#![feature(let_chains)] #![feature(strict_overflow_ops)] #![feature(pointer_is_aligned_to)] #![feature(unqualified_local_imports)] diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index 0258a76c3e70..837e1b31cac5 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -117,7 +117,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut info = std::mem::MaybeUninit::::uninit(); unsafe { if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { - if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() + let info = info.assume_init(); + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = info.dli_fname; + if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap() != _lib_path.to_str().unwrap() { return None; diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 18af82148763..b5ed5ea837b3 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -85,6 +85,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Now we make a function call, and pass `data` as first and only argument. let f_instance = this.get_ptr_fn(try_fn)?.as_instance()?; trace!("try_fn: {:?}", f_instance); + #[allow(clippy::cloned_ref_to_slice_refs)] // the code is clearer as-is this.call_function( f_instance, ExternAbi::Rust, diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index a9d09ac7a9d6..40bfe7f845fe 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -142,24 +142,19 @@ def test_cargo_miri_run(): ) def test_cargo_miri_test(): - # rustdoc is not run on foreign targets - is_foreign = ARGS.target is not None - default_ref = "test.cross-target.stdout.ref" if is_foreign else "test.default.stdout.ref" - filter_ref = "test.filter.cross-target.stdout.ref" if is_foreign else "test.filter.stdout.ref" - test("`cargo miri test`", cargo_miri("test"), - default_ref, "test.empty.ref", + "test.default.stdout.ref", "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-seed=4242"}, ) test("`cargo miri test` (no isolation, no doctests)", cargo_miri("test") + ["--bins", "--tests"], # no `--lib`, we disabled that in `Cargo.toml` - "test.cross-target.stdout.ref", "test.empty.ref", + "test.no-doc.stdout.ref", "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) test("`cargo miri test` (with filter)", cargo_miri("test") + ["--", "--format=pretty", "pl"], - filter_ref, "test.empty.ref", + "test.filter.stdout.ref", "test.empty.ref", ) test("`cargo miri test` (test target)", cargo_miri("test") + ["--test", "test", "--", "--format=pretty"], @@ -171,7 +166,7 @@ def test_cargo_miri_test(): ) test("`cargo miri t` (subcrate, no isolation)", cargo_miri("t") + ["-p", "subcrate"], - "test.subcrate.cross-target.stdout.ref" if is_foreign else "test.subcrate.stdout.ref", + "test.subcrate.stdout.ref", "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) @@ -181,12 +176,12 @@ def test_cargo_miri_test(): ) test("`cargo miri test` (custom target dir)", cargo_miri("test") + ["--target-dir=custom-test"], - default_ref, "test.empty.ref", + "test.default.stdout.ref", "test.empty.ref", ) del os.environ["CARGO_TARGET_DIR"] # this overrides `build.target-dir` passed by `--config`, so unset it test("`cargo miri test` (config-cli)", cargo_miri("test") + ["--config=build.target-dir=\"config-cli\""], - default_ref, "test.empty.ref", + "test.default.stdout.ref", "test.empty.ref", ) if ARGS.multi_target: test_cargo_miri_multi_target() diff --git a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref deleted file mode 100644 index 59b4deb1ff32..000000000000 --- a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref +++ /dev/null @@ -1,12 +0,0 @@ - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME - -imported main - -running 1 test -test simple ... ok - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME - diff --git a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref index 567c5db07d06..a376530a8cfb 100644 --- a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref @@ -20,3 +20,13 @@ running 6 tests ...i.. test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.no-doc.stdout.ref similarity index 100% rename from src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref rename to src/tools/miri/test-cargo-miri/test.no-doc.stdout.ref diff --git a/src/tools/miri/test-cargo-miri/test.subcrate.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.subcrate.cross-target.stdout.ref deleted file mode 100644 index 436e6e4fbbbc..000000000000 --- a/src/tools/miri/test-cargo-miri/test.subcrate.cross-target.stdout.ref +++ /dev/null @@ -1,11 +0,0 @@ - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - -subcrate testing diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs index 2097126e16b9..4a87411d755c 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs @@ -1,5 +1,5 @@ //@error-in-other-file: aborted -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" #![feature(allocator_api)] diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr index 3642f3f28ca2..fa84da841fd4 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr @@ -2,7 +2,7 @@ memory allocation of 4 bytes failed error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs index ffa0443ce507..0e9163641690 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs @@ -4,6 +4,6 @@ fn main() { } unsafe { - let _ = malloc(0); //~ ERROR: calling a function with ABI C using caller ABI Rust + let _ = malloc(0); //~ ERROR: calling a function with calling convention "C" using caller calling convention "Rust" }; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr index bf1fbb7721f1..787301829231 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with ABI C using caller ABI Rust +error: Undefined Behavior: calling a function with calling convention "C" using caller calling convention "Rust" --> tests/fail/function_calls/check_arg_abi.rs:LL:CC | LL | let _ = malloc(0); - | ^^^^^^^^^ calling a function with ABI C using caller ABI Rust + | ^^^^^^^^^ calling a function with calling convention "C" using caller calling convention "Rust" | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs index 6a7a26710d16..177e38105e6f 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs @@ -9,7 +9,7 @@ fn main() { // Make sure we check the ABI when Miri itself invokes a function // as part of a shim implementation. std::intrinsics::catch_unwind( - //~^ ERROR: calling a function with calling convention C using calling convention Rust + //~^ ERROR: calling a function with calling convention "C" using calling convention "Rust" std::mem::transmute::(try_fn), std::ptr::null_mut(), |_, _| unreachable!(), diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr index 6b0692e1c6e8..20182ac92367 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with calling convention C using calling convention Rust +error: Undefined Behavior: calling a function with calling convention "C" using calling convention "Rust" --> tests/fail/function_calls/check_callback_abi.rs:LL:CC | LL | / std::intrinsics::catch_unwind( @@ -7,7 +7,7 @@ LL | | std::mem::transmute::(try_fn), LL | | std::ptr::null_mut(), LL | | |_, _| unreachable!(), LL | | ); - | |_________^ calling a function with calling convention C using calling convention Rust + | |_________^ calling a function with calling convention "C" using calling convention "Rust" | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr index e4302ad1d3a5..46a32d1487ea 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention Rust using calling convention C +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" --> tests/fail/function_calls/exported_symbol_abi_mismatch.rs:LL:CC | LL | foo(); - | ^^^^^ calling a function with calling convention Rust using calling convention C + | ^^^^^ calling a function with calling convention "Rust" using calling convention "C" | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr index 9f40c48b338e..387252899190 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention Rust using calling convention C +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" --> tests/fail/function_calls/exported_symbol_abi_mismatch.rs:LL:CC | LL | std::mem::transmute::(foo)(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention Rust using calling convention C + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention "Rust" using calling convention "C" | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr index e4302ad1d3a5..46a32d1487ea 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention Rust using calling convention C +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" --> tests/fail/function_calls/exported_symbol_abi_mismatch.rs:LL:CC | LL | foo(); - | ^^^^^ calling a function with calling convention Rust using calling convention C + | ^^^^^ calling a function with calling convention "Rust" using calling convention "C" | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs index 50a0e8e6edef..1950e162c070 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs @@ -12,7 +12,7 @@ fn main() { #[cfg(fn_ptr)] unsafe { std::mem::transmute::(foo)(); - //~[fn_ptr]^ ERROR: calling a function with calling convention Rust using calling convention C + //~[fn_ptr]^ ERROR: calling a function with calling convention "Rust" using calling convention "C" } // `Instance` caching should not suppress ABI check. @@ -28,8 +28,8 @@ fn main() { } unsafe { foo(); - //~[no_cache]^ ERROR: calling a function with calling convention Rust using calling convention C - //~[cache]| ERROR: calling a function with calling convention Rust using calling convention C + //~[no_cache]^ ERROR: calling a function with calling convention "Rust" using calling convention "C" + //~[cache]| ERROR: calling a function with calling convention "Rust" using calling convention "C" } } } diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr index b2a501db7763..7cb2bf996781 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr index b2a501db7763..7cb2bf996781 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs index 1382e9571f31..9d993786d57f 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs @@ -1,5 +1,5 @@ //@revisions: extern_block definition both -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs index 6e0e0ca9f53e..dd3246d81203 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs @@ -1,4 +1,4 @@ -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr index 2c9bea1724d6..ba96e595bee2 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr @@ -7,7 +7,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs index 0e8d3d08c128..3d355bad626a 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs @@ -1,4 +1,4 @@ -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr index 0634298a38f3..7e1f4160cc0e 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr @@ -7,7 +7,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.rs b/src/tools/miri/tests/fail/panic/abort_unwind.rs index e313d9c11dec..bd819362da41 100644 --- a/src/tools/miri/tests/fail/panic/abort_unwind.rs +++ b/src/tools/miri/tests/fail/panic/abort_unwind.rs @@ -1,5 +1,5 @@ //@error-in-other-file: the program aborted execution -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.stderr b/src/tools/miri/tests/fail/panic/abort_unwind.stderr index 3a63cb38ad06..e6668b09f66b 100644 --- a/src/tools/miri/tests/fail/panic/abort_unwind.stderr +++ b/src/tools/miri/tests/fail/panic/abort_unwind.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/panic/double_panic.rs b/src/tools/miri/tests/fail/panic/double_panic.rs index ddc75521ecad..4d8f4cb6fb7b 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.rs +++ b/src/tools/miri/tests/fail/panic/double_panic.rs @@ -1,4 +1,4 @@ -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr index 16e933be4348..67f88955defb 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.stderr +++ b/src/tools/miri/tests/fail/panic/double_panic.stderr @@ -14,7 +14,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.rs b/src/tools/miri/tests/fail/panic/panic_abort1.rs index 7552c7b7e808..06cb673778a2 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort1.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort1.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.stderr b/src/tools/miri/tests/fail/panic/panic_abort1.stderr index c950b2b4ea63..6d56874ebde9 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort1.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort1.stderr @@ -4,13 +4,15 @@ panicking from libstd note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.rs b/src/tools/miri/tests/fail/panic/panic_abort2.rs index 624f99335455..c011b3ee7eb8 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort2.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort2.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.stderr b/src/tools/miri/tests/fail/panic/panic_abort2.stderr index 9a9266ec493a..dbb56f13f486 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort2.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort2.stderr @@ -4,13 +4,15 @@ thread 'main' panicked at tests/fail/panic/panic_abort2.rs:LL:CC: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.rs b/src/tools/miri/tests/fail/panic/panic_abort3.rs index d1435b55946c..911dc4a44abd 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort3.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort3.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.stderr b/src/tools/miri/tests/fail/panic/panic_abort3.stderr index f04a2b0f3f14..7f0564879e47 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort3.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort3.stderr @@ -4,13 +4,15 @@ panicking from libcore note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.rs b/src/tools/miri/tests/fail/panic/panic_abort4.rs index 54b9c9cbfdb0..696fdff74223 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort4.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort4.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.stderr b/src/tools/miri/tests/fail/panic/panic_abort4.stderr index e71c4879ea32..ce6910b99331 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort4.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort4.stderr @@ -4,13 +4,15 @@ thread 'main' panicked at tests/fail/panic/panic_abort4.rs:LL:CC: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs index 6f627c416b0f..6119e8604b46 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs @@ -1,6 +1,6 @@ //! This is a regression test for : The precondition //! check in `ptr::swap_nonoverlapping` was incorrectly disabled in Miri. -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr index 80dd2f39b426..f57487e3ffe1 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr @@ -9,7 +9,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs index 5f00dbf25736..952f9697fc79 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs @@ -1,4 +1,4 @@ -//@error-in-other-file: Undefined Behavior: calling a function with calling convention C using calling convention Rust +//@error-in-other-file: Undefined Behavior: calling a function with calling convention "C" using calling convention "Rust" #![feature(explicit_tail_calls)] #![allow(incomplete_features)] diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr index 5061c9e8dc3f..61ddea644720 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention C using calling convention Rust +error: Undefined Behavior: calling a function with calling convention "C" using calling convention "Rust" --> RUSTLIB/core/src/ops/function.rs:LL:CC | LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using calling convention Rust + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention "C" using calling convention "Rust" | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/terminate-terminator.rs b/src/tools/miri/tests/fail/terminate-terminator.rs index 465625c75725..31ae829a2de7 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.rs +++ b/src/tools/miri/tests/fail/terminate-terminator.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmir-opt-level=3 -Zinline-mir-hint-threshold=1000 -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr index f2548bf5cdb2..d16119a30e6f 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.stderr +++ b/src/tools/miri/tests/fail/terminate-terminator.stderr @@ -13,7 +13,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.rs b/src/tools/miri/tests/fail/unwind-action-terminate.rs index 465e07c8db4b..f0fbcfd88675 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.rs +++ b/src/tools/miri/tests/fail/unwind-action-terminate.rs @@ -1,5 +1,5 @@ //@error-in-other-file: aborted execution -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr index 7b9a4383fc4c..222d4fb28669 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr +++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr deleted file mode 100644 index a437ca342585..000000000000 --- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: Undefined Behavior: Race condition detected between (1) 4-byte atomic store on thread `unnamed-ID` and (2) 2-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here - --> tests/fail/weak_memory/racing_mixed_size.rs:LL:CC - | -LL | std::intrinsics::atomic_load_relaxed(hi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) 4-byte atomic store on thread `unnamed-ID` and (2) 2-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here - | -help: and (1) occurred earlier here - --> tests/fail/weak_memory/racing_mixed_size.rs:LL:CC - | -LL | x.store(1, Relaxed); - | ^^^^^^^^^^^^^^^^^^^ - = help: overlapping unsynchronized atomic accesses must use the same access size - = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE (of the first span) on thread `unnamed-ID`: - = note: inside closure at tests/fail/weak_memory/racing_mixed_size.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index c2b6a7e68be5..c76e7f2eebd2 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -12,7 +12,6 @@ fn main() { mut_raw_mut(); partially_invalidate_mut(); drop_after_sharing(); - // direct_mut_to_const_raw(); two_raw(); shr_and_raw(); disjoint_mutable_subborrows(); diff --git a/src/tools/miri/tests/pass/coroutine.rs b/src/tools/miri/tests/pass/coroutine.rs index 9ec9b1fc5bc4..96b60b515cb6 100644 --- a/src/tools/miri/tests/pass/coroutine.rs +++ b/src/tools/miri/tests/pass/coroutine.rs @@ -183,18 +183,18 @@ fn basic() { fn smoke_resume_arg() { fn drain + Unpin, R, Y>( - gen: &mut G, + gen_: &mut G, inout: Vec<(R, CoroutineState)>, ) where Y: Debug + PartialEq, G::Return: Debug + PartialEq, { - let mut gen = Pin::new(gen); + let mut gen_ = Pin::new(gen_); for (input, out) in inout { - assert_eq!(gen.as_mut().resume(input), out); + assert_eq!(gen_.as_mut().resume(input), out); // Test if the coroutine is valid (according to type invariants). - let _ = unsafe { ManuallyDrop::new(ptr::read(gen.as_mut().get_unchecked_mut())) }; + let _ = unsafe { ManuallyDrop::new(ptr::read(gen_.as_mut().get_unchecked_mut())) }; } } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 47f086f7340d..48633c0a7fe6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -2,7 +2,7 @@ //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f -#![feature(avx512_target_feature, stdarch_x86_avx512)] +#![feature(stdarch_x86_avx512)] use core::mem::transmute; #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index db5930638901..0ec2f679d80b 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -2,7 +2,6 @@ //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs index 882b5e3f7952..b58d68e2ef9e 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -6,7 +6,6 @@ // be interpreted as integers; signedness does not make sense for them, but // __mXXXi happens to be defined in terms of signed integers. #![allow(overflowing_literals)] -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs index 68964728e4ea..c7c9eb5e3951 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs @@ -8,7 +8,6 @@ // be interpreted as integers; signedness does not make sense for them, but // __mXXXi happens to be defined in terms of signed integers. #![allow(overflowing_literals)] -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index c37cf15d40ab..46472b51f9cd 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -44,8 +44,7 @@ pub fn flagsplit(flags: &str) -> Vec { fn build_native_lib() -> PathBuf { let cc = env::var("CC").unwrap_or_else(|_| "cc".into()); // Target directory that we can write to. - let so_target_dir = - Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-native-lib"); + let so_target_dir = Path::new(env!("CARGO_TARGET_TMPDIR")).join("miri-native-lib"); // Create the directory if it does not already exist. std::fs::create_dir_all(&so_target_dir) .expect("Failed to create directory for shared object file"); @@ -101,7 +100,7 @@ fn miri_config( let mut config = Config { target: Some(target.to_owned()), program, - out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri_ui"), + out_dir: PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("miri_ui"), threads: std::env::var("MIRI_TEST_THREADS") .ok() .map(|threads| NonZero::new(threads.parse().unwrap()).unwrap()), @@ -319,10 +318,10 @@ fn main() -> Result<()> { let mut args = std::env::args_os(); // Skip the program name and check whether this is a `./miri run-dep` invocation - if let Some(first) = args.nth(1) { - if first == "--miri-run-dep-mode" { - return run_dep_mode(target, args); - } + if let Some(first) = args.nth(1) + && first == "--miri-run-dep-mode" + { + return run_dep_mode(target, args); } ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 3b5b31bed14a..0b3893770111 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -156,9 +156,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "cc" -version = "1.2.21" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "shlex", ] @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.48" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8c97f3a6f02b9e24cadc12aaba75201d18754b53ea0a9d99642f806ccdb4c9" +checksum = "c91d3baa3bcd889d60e6ef28874126a0b384fd225ab83aa6d8a801c519194ce1" dependencies = [ "clap", ] @@ -547,9 +547,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -644,21 +644,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -667,31 +668,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -699,67 +680,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -779,9 +747,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -811,9 +779,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -824,9 +792,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -882,9 +850,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" @@ -1274,6 +1242,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "precomputed-hash" version = "0.1.1" @@ -1710,9 +1687,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -1821,12 +1798,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1924,9 +1895,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bcbdcad8fb2e316072ba6bbe09419afdb550285668ac2534f4230a6f2da0ee" +checksum = "0b9c5f0bc545ea3b20b423e33b9b457764de0b3730cd957f6c6aa6c301785f6e" dependencies = [ "phf", "phf_codegen", @@ -2099,9 +2070,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -2115,23 +2086,17 @@ dependencies = [ "bitflags 2.9.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -2141,9 +2106,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -2173,10 +2138,21 @@ dependencies = [ ] [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -2185,9 +2161,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 58c8d21bd7a4..1a9614bacec8 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,10 +1,10 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args}; +use rustc_builtin_macros::asm::{AsmArg, parse_asm_args}; use crate::rewrite::RewriteContext; #[allow(dead_code)] -pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { +pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option> { let ts = mac.args.tokens.clone(); let mut parser = super::build_parser(context, ts); parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 4195258af885..9bb06c31c5c0 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -429,10 +429,13 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "winapi-util", "winapi-x86_64-pc-windows-gnu", "windows", + "windows-collections", "windows-core", + "windows-future", "windows-implement", "windows-interface", "windows-link", + "windows-numerics", "windows-result", "windows-strings", "windows-sys", diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 2c20e21f451a..1d0ddd56eec5 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1183,47 +1183,39 @@ ui/impl-trait/explicit-generic-args-with-impl-trait/issue-87718.rs ui/impl-trait/in-trait/issue-102140.rs ui/impl-trait/in-trait/issue-102301.rs ui/impl-trait/in-trait/issue-102571.rs -ui/impl-trait/issue-100075-2.rs -ui/impl-trait/issue-100075.rs -ui/impl-trait/issue-100187.rs -ui/impl-trait/issue-102605.rs -ui/impl-trait/issue-103181-1.rs -ui/impl-trait/issue-103181-2.rs -ui/impl-trait/issue-103599.rs -ui/impl-trait/issue-108591.rs -ui/impl-trait/issue-108592.rs -ui/impl-trait/issue-35668.rs -ui/impl-trait/issue-36792.rs -ui/impl-trait/issue-46959.rs -ui/impl-trait/issue-49556.rs -ui/impl-trait/issue-49579.rs -ui/impl-trait/issue-49685.rs -ui/impl-trait/issue-51185.rs -ui/impl-trait/issue-54966.rs -ui/impl-trait/issue-55872-1.rs -ui/impl-trait/issue-55872-2.rs -ui/impl-trait/issue-55872-3.rs -ui/impl-trait/issue-55872.rs -ui/impl-trait/issue-56445.rs -ui/impl-trait/issue-68532.rs -ui/impl-trait/issue-72911.rs -ui/impl-trait/issue-87450.rs -ui/impl-trait/issue-99073-2.rs -ui/impl-trait/issue-99073.rs -ui/impl-trait/issue-99642-2.rs -ui/impl-trait/issue-99642.rs -ui/impl-trait/issue-99914.rs +ui/impl-trait/issues/issue-100075-2.rs +ui/impl-trait/issues/issue-100075.rs +ui/impl-trait/issues/issue-100187.rs +ui/impl-trait/issues/issue-102605.rs +ui/impl-trait/issues/issue-103181-1.rs +ui/impl-trait/issues/issue-103181-2.rs +ui/impl-trait/issues/issue-103599.rs ui/impl-trait/issues/issue-104815.rs ui/impl-trait/issues/issue-105826.rs +ui/impl-trait/issues/issue-108591.rs +ui/impl-trait/issues/issue-108592.rs ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.rs +ui/impl-trait/issues/issue-35668.rs +ui/impl-trait/issues/issue-36792.rs ui/impl-trait/issues/issue-42479.rs +ui/impl-trait/issues/issue-46959.rs ui/impl-trait/issues/issue-49376.rs +ui/impl-trait/issues/issue-49556.rs +ui/impl-trait/issues/issue-49579.rs +ui/impl-trait/issues/issue-49685.rs +ui/impl-trait/issues/issue-51185.rs ui/impl-trait/issues/issue-52128.rs ui/impl-trait/issues/issue-53457.rs ui/impl-trait/issues/issue-54600.rs ui/impl-trait/issues/issue-54840.rs ui/impl-trait/issues/issue-54895.rs +ui/impl-trait/issues/issue-54966.rs ui/impl-trait/issues/issue-55608-captures-empty-region.rs +ui/impl-trait/issues/issue-55872-1.rs +ui/impl-trait/issues/issue-55872-2.rs +ui/impl-trait/issues/issue-55872-3.rs +ui/impl-trait/issues/issue-55872.rs +ui/impl-trait/issues/issue-56445.rs ui/impl-trait/issues/issue-57464-unexpected-regions.rs ui/impl-trait/issues/issue-57979-deeply-nested-impl-trait-in-assoc-proj.rs ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs @@ -1233,8 +1225,10 @@ ui/impl-trait/issues/issue-58956.rs ui/impl-trait/issues/issue-62742.rs ui/impl-trait/issues/issue-65581.rs ui/impl-trait/issues/issue-67830.rs +ui/impl-trait/issues/issue-68532.rs ui/impl-trait/issues/issue-70877.rs ui/impl-trait/issues/issue-70971.rs +ui/impl-trait/issues/issue-72911.rs ui/impl-trait/issues/issue-74282.rs ui/impl-trait/issues/issue-77987.rs ui/impl-trait/issues/issue-78722-2.rs @@ -1251,12 +1245,18 @@ ui/impl-trait/issues/issue-86719.rs ui/impl-trait/issues/issue-86800.rs ui/impl-trait/issues/issue-87295.rs ui/impl-trait/issues/issue-87340.rs +ui/impl-trait/issues/issue-87450.rs ui/impl-trait/issues/issue-88236-2.rs ui/impl-trait/issues/issue-88236.rs ui/impl-trait/issues/issue-89312.rs ui/impl-trait/issues/issue-92305.rs ui/impl-trait/issues/issue-93788.rs +ui/impl-trait/issues/issue-99073-2.rs +ui/impl-trait/issues/issue-99073.rs ui/impl-trait/issues/issue-99348-impl-compatibility.rs +ui/impl-trait/issues/issue-99642-2.rs +ui/impl-trait/issues/issue-99642.rs +ui/impl-trait/issues/issue-99914.rs ui/implied-bounds/issue-100690.rs ui/implied-bounds/issue-101951.rs ui/implied-bounds/issue-110161.rs diff --git a/tests/crashes/123456.rs b/tests/crashes/123456.rs deleted file mode 100644 index ed7cbada3f85..000000000000 --- a/tests/crashes/123456.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #123456 - -trait Project { - const SELF: Self; -} - -fn take1( - _: Project< - SELF = { - j2.join().unwrap(); - }, - >, -) { -} - -pub fn main() {} diff --git a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs index 99eb92f432c2..76f6aaee6dce 100644 --- a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs +++ b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs @@ -15,12 +15,14 @@ where impl<'a, T, const S: usize> Iterator for BufferIter<'a, T, S> { //~^ error: the trait bound - //~^^ error: unconstrained generic constant + //~| error: unconstrained generic constant type Item = &'a T; fn next(&mut self) -> Option { //~^ error: the trait bound - //~^^ error: unconstrained generic constant + //~| error: unconstrained generic constant + //~| error: the trait bound + //~| error: unconstrained generic constant None } } diff --git a/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff new file mode 100644 index 000000000000..fcabcdbcfef2 --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff @@ -0,0 +1,18 @@ +- // MIR for `overlapping` before GVN ++ // MIR for `overlapping` after GVN + + fn overlapping(_1: Adt) -> () { + let mut _0: (); + let mut _2: *mut Adt; + let mut _3: u32; + let mut _4: &Adt; + + bb0: { + _2 = &raw mut _1; + _4 = &(*_2); + _3 = copy (((*_4) as variant#1).0: u32); + (*_2) = Adt::Some(copy _3); + return; + } + } + diff --git a/tests/mir-opt/gvn_overlapping.rs b/tests/mir-opt/gvn_overlapping.rs new file mode 100644 index 000000000000..99113445e683 --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.rs @@ -0,0 +1,36 @@ +//@ test-mir-pass: GVN + +#![feature(custom_mir, core_intrinsics)] + +// Check that we do not create overlapping assignments. + +use std::intrinsics::mir::*; + +// EMIT_MIR gvn_overlapping.overlapping.GVN.diff +#[custom_mir(dialect = "runtime")] +fn overlapping(_17: Adt) { + // CHECK-LABEL: fn overlapping( + // CHECK: let mut [[PTR:.*]]: *mut Adt; + // CHECK: (*[[PTR]]) = Adt::Some(copy {{.*}}); + mir! { + let _33: *mut Adt; + let _48: u32; + let _73: &Adt; + { + _33 = core::ptr::addr_of_mut!(_17); + _73 = &(*_33); + _48 = Field(Variant((*_73), 1), 0); + (*_33) = Adt::Some(_48); + Return() + } + } +} + +fn main() { + overlapping(Adt::Some(0)); +} + +enum Adt { + None, + Some(u32), +} diff --git a/tests/run-make/arm64ec-import-export-static/export.rs b/tests/run-make/arm64ec-import-export-static/export.rs deleted file mode 100644 index 98b3a66d80c0..000000000000 --- a/tests/run-make/arm64ec-import-export-static/export.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![crate_type = "dylib"] -#![allow(internal_features)] -#![feature(no_core, lang_items)] -#![no_core] -#![no_std] - -// This is needed because of #![no_core]: -#[lang = "sized"] -trait Sized {} -#[lang = "sync"] -trait Sync {} -impl Sync for i32 {} -#[lang = "copy"] -pub trait Copy {} -impl Copy for i32 {} -#[lang = "drop_in_place"] -pub unsafe fn drop_in_place(_: *mut T) {} -#[no_mangle] -extern "system" fn _DllMainCRTStartup(_: *const u8, _: u32, _: *const u8) -> u32 { - 1 -} - -pub static VALUE: i32 = 42; diff --git a/tests/run-make/arm64ec-import-export-static/import.rs b/tests/run-make/arm64ec-import-export-static/import.rs deleted file mode 100644 index 9d52db251250..000000000000 --- a/tests/run-make/arm64ec-import-export-static/import.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![crate_type = "cdylib"] -#![allow(internal_features)] -#![feature(no_core)] -#![no_std] -#![no_core] - -extern crate export; - -#[no_mangle] -pub extern "C" fn func() -> i32 { - export::VALUE -} diff --git a/tests/run-make/arm64ec-import-export-static/rmake.rs b/tests/run-make/arm64ec-import-export-static/rmake.rs deleted file mode 100644 index 7fa31144810d..000000000000 --- a/tests/run-make/arm64ec-import-export-static/rmake.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Test that a static can be exported from one crate and imported into another. -// -// This was broken for Arm64EC as only functions, not variables, should be -// decorated with `#`. -// See https://github.com/rust-lang/rust/issues/138541 - -//@ needs-llvm-components: aarch64 -//@ only-windows - -use run_make_support::rustc; - -fn main() { - rustc().input("export.rs").target("aarch64-pc-windows-msvc").panic("abort").run(); - rustc().input("import.rs").target("aarch64-pc-windows-msvc").panic("abort").run(); -} diff --git a/tests/run-make/core-no-oom-handling/rmake.rs b/tests/run-make/core-no-oom-handling/rmake.rs index a9e2b33e2107..5194d7731144 100644 --- a/tests/run-make/core-no-oom-handling/rmake.rs +++ b/tests/run-make/core-no-oom-handling/rmake.rs @@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root}; fn main() { rustc() - .edition("2021") + .edition("2024") .arg("-Dwarnings") .crate_type("rlib") .input(source_root().join("library/core/src/lib.rs")) diff --git a/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs b/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs index 2727effe8188..d28c8463016c 100644 --- a/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs +++ b/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs @@ -11,6 +11,13 @@ //@ needs-dynamic-linking //@ only-nightly (requires unstable rustc flag) +// This test trips a check in the MSVC linker for an outdated processor: +// "LNK1322: cannot avoid potential ARM hazard (Cortex-A53 MPCore processor bug #843419)" +// Until MSVC removes this check: +// https://developercommunity.microsoft.com/t/Remove-checking-for-and-fixing-Cortex-A/10905134 +// we'll need to disable this test on Arm64 Windows. +//@ ignore-aarch64-pc-windows-msvc + #![deny(warnings)] use run_make_support::{dynamic_lib_name, rfs, rust_lib_name, rustc}; diff --git a/tests/run-make/sanitizer-dylib-link/program.rs b/tests/run-make/sanitizer-dylib-link/program.rs index dbf885d343fe..1026c7f89ba9 100644 --- a/tests/run-make/sanitizer-dylib-link/program.rs +++ b/tests/run-make/sanitizer-dylib-link/program.rs @@ -1,4 +1,4 @@ -#[cfg_attr(windows, link(name = "library", kind = "raw-dylib"))] +#[cfg_attr(windows, link(name = "library.dll.lib", modifiers = "+verbatim"))] #[cfg_attr(not(windows), link(name = "library"))] extern "C" { fn overflow(); diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs index 22b9b029a404..2c78b794a8d7 100644 --- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs +++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs @@ -7,8 +7,6 @@ //@ run-pass //@ needs-subprocess -#![feature(avx512_target_feature)] - #![allow(overflowing_literals)] #![allow(unused_variables)] diff --git a/tests/ui/abi/simd-abi-checks-avx.rs b/tests/ui/abi/simd-abi-checks-avx.rs index 772512702ece..7432381d15b7 100644 --- a/tests/ui/abi/simd-abi-checks-avx.rs +++ b/tests/ui/abi/simd-abi-checks-avx.rs @@ -2,7 +2,6 @@ //@ build-fail //@ compile-flags: -C target-feature=-avx -#![feature(avx512_target_feature)] #![feature(portable_simd)] #![feature(simd_ffi)] #![allow(improper_ctypes_definitions)] diff --git a/tests/ui/abi/simd-abi-checks-avx.stderr b/tests/ui/abi/simd-abi-checks-avx.stderr index 48db30bf4537..7489ca019460 100644 --- a/tests/ui/abi/simd-abi-checks-avx.stderr +++ b/tests/ui/abi/simd-abi-checks-avx.stderr @@ -1,5 +1,5 @@ error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:60:11 + --> $DIR/simd-abi-checks-avx.rs:59:11 | LL | f(g()); | ^^^ function called here @@ -7,7 +7,7 @@ LL | f(g()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:60:9 + --> $DIR/simd-abi-checks-avx.rs:59:9 | LL | f(g()); | ^^^^^^ function called here @@ -15,7 +15,7 @@ LL | f(g()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:66:14 + --> $DIR/simd-abi-checks-avx.rs:65:14 | LL | gavx(favx()); | ^^^^^^ function called here @@ -23,7 +23,7 @@ LL | gavx(favx()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:66:9 + --> $DIR/simd-abi-checks-avx.rs:65:9 | LL | gavx(favx()); | ^^^^^^^^^^^^ function called here @@ -31,7 +31,7 @@ LL | gavx(favx()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:76:19 + --> $DIR/simd-abi-checks-avx.rs:75:19 | LL | w(Wrapper(g())); | ^^^ function called here @@ -39,7 +39,7 @@ LL | w(Wrapper(g())); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:76:9 + --> $DIR/simd-abi-checks-avx.rs:75:9 | LL | w(Wrapper(g())); | ^^^^^^^^^^^^^^^ function called here @@ -47,7 +47,7 @@ LL | w(Wrapper(g())); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:90:9 + --> $DIR/simd-abi-checks-avx.rs:89:9 | LL | some_extern(); | ^^^^^^^^^^^^^ function called here @@ -55,7 +55,7 @@ LL | some_extern(); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:25:1 + --> $DIR/simd-abi-checks-avx.rs:24:1 | LL | unsafe extern "C" fn g() -> __m256 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -63,7 +63,7 @@ LL | unsafe extern "C" fn g() -> __m256 { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:20:1 + --> $DIR/simd-abi-checks-avx.rs:19:1 | LL | unsafe extern "C" fn f(_: __m256) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -71,7 +71,7 @@ LL | unsafe extern "C" fn f(_: __m256) { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:15:1 + --> $DIR/simd-abi-checks-avx.rs:14:1 | LL | unsafe extern "C" fn w(_: Wrapper) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -79,7 +79,7 @@ LL | unsafe extern "C" fn w(_: Wrapper) { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:54:8 + --> $DIR/simd-abi-checks-avx.rs:53:8 | LL | || g() | ^^^ function called here @@ -87,7 +87,7 @@ LL | || g() = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) note: the above error was encountered while instantiating `fn in_closure::{closure#0}` - --> $DIR/simd-abi-checks-avx.rs:82:9 + --> $DIR/simd-abi-checks-avx.rs:81:9 | LL | in_closure()(); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs index aa731c35dda8..35e1d037f388 100644 --- a/tests/ui/asm/aarch64/parse-error.rs +++ b/tests/ui/asm/aarch64/parse-error.rs @@ -96,10 +96,8 @@ global_asm!("", options(FOO)); //~^ ERROR expected one of global_asm!("", options(nomem FOO)); //~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` global_asm!("", options(nomem, FOO)); //~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` global_asm!("{}", options(), const FOO); global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index b5e1169e5f6b..45f9e7989c2e 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -218,68 +218,56 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` LL | global_asm!("", options(FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:97:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)` or `,`, found `FOO` --> $DIR/parse-error.rs:97:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:100:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:100:32 + --> $DIR/parse-error.rs:99:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:104:29 + --> $DIR/parse-error.rs:102:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:106:33 + --> $DIR/parse-error.rs:104:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:108:34 + --> $DIR/parse-error.rs:106:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:110:19 + --> $DIR/parse-error.rs:108:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:112:28 + --> $DIR/parse-error.rs:110:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:114:30 + --> $DIR/parse-error.rs:112:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:116:35 + --> $DIR/parse-error.rs:114:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -287,7 +275,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:116:35 + --> $DIR/parse-error.rs:114:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -295,19 +283,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:119:28 + --> $DIR/parse-error.rs:117:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:121:30 + --> $DIR/parse-error.rs:119:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:123:13 + --> $DIR/parse-error.rs:121:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -315,7 +303,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:125:20 + --> $DIR/parse-error.rs:123:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -418,6 +406,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 59 previous errors +error: aborting due to 57 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs index 4d7b522f5fc5..d135ccae1280 100644 --- a/tests/ui/asm/parse-error.rs +++ b/tests/ui/asm/parse-error.rs @@ -113,11 +113,9 @@ global_asm!("", options(FOO)); global_asm!("", options(FOO,)); //~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("", options(nomem FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)` or `,`, found `FOO` +//~^ ERROR expected one of `)` or `,`, found `FOO` global_asm!("", options(nomem, FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` +//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("{}", options(), const FOO); global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 74647372a355..0bba1fd8d9b6 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -270,74 +270,62 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` LL | global_asm!("", options(FOO,)); | ^^^ expected one of `)`, `att_syntax`, or `raw` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:115:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)` or `,`, found `FOO` --> $DIR/parse-error.rs:115:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:118:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:118:32 + --> $DIR/parse-error.rs:117:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:122:29 + --> $DIR/parse-error.rs:120:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:124:33 + --> $DIR/parse-error.rs:122:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:126:34 + --> $DIR/parse-error.rs:124:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:128:19 + --> $DIR/parse-error.rs:126:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:130:28 + --> $DIR/parse-error.rs:128:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:132:30 + --> $DIR/parse-error.rs:130:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:134:17 + --> $DIR/parse-error.rs:132:17 | LL | global_asm!("", clobber_abi("C"), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -345,7 +333,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -353,19 +341,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:139:28 + --> $DIR/parse-error.rs:137:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:141:30 + --> $DIR/parse-error.rs:139:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:143:13 + --> $DIR/parse-error.rs:141:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -373,7 +361,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:145:20 + --> $DIR/parse-error.rs:143:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -381,37 +369,37 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: the `in` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:148:19 + --> $DIR/parse-error.rs:146:19 | LL | global_asm!("{}", in(reg)); | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `out` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:150:19 + --> $DIR/parse-error.rs:148:19 | LL | global_asm!("{}", out(reg)); | ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it error: the `lateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:152:19 + --> $DIR/parse-error.rs:150:19 | LL | global_asm!("{}", lateout(reg)); | ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:154:19 + --> $DIR/parse-error.rs:152:19 | LL | global_asm!("{}", inout(reg)); | ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inlateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:156:19 + --> $DIR/parse-error.rs:154:19 | LL | global_asm!("{}", inlateout(reg)); | ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `label` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:158:19 + --> $DIR/parse-error.rs:156:19 | LL | global_asm!("{}", label(reg)); | ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it @@ -476,6 +464,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 72 previous errors +error: aborting due to 70 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/x86_64/evex512-implicit-feature.rs b/tests/ui/asm/x86_64/evex512-implicit-feature.rs index ea2acd424e2c..ec5da7c7fa48 100644 --- a/tests/ui/asm/x86_64/evex512-implicit-feature.rs +++ b/tests/ui/asm/x86_64/evex512-implicit-feature.rs @@ -2,7 +2,6 @@ //@ only-x86_64 //@ compile-flags: --crate-type=lib -C target-cpu=skylake -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] use std::arch::x86_64::*; diff --git a/tests/ui/asm/x86_64/target-feature-attr.rs b/tests/ui/asm/x86_64/target-feature-attr.rs index 6bb277ac1659..2193117caeb6 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.rs +++ b/tests/ui/asm/x86_64/target-feature-attr.rs @@ -2,8 +2,6 @@ // Set the base cpu explicitly, in case the default has been changed. //@ compile-flags: -C target-cpu=x86-64 -#![feature(avx512_target_feature)] - use std::arch::asm; #[target_feature(enable = "avx")] diff --git a/tests/ui/asm/x86_64/target-feature-attr.stderr b/tests/ui/asm/x86_64/target-feature-attr.stderr index 0cd571ac8cce..c852726ee7ff 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.stderr +++ b/tests/ui/asm/x86_64/target-feature-attr.stderr @@ -1,23 +1,23 @@ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:40 + --> $DIR/target-feature-attr.rs:18:40 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:55 + --> $DIR/target-feature-attr.rs:18:55 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:70 + --> $DIR/target-feature-attr.rs:18:70 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^^^^^^ error: register class `kreg` requires at least one of the following target features: avx512bw, avx512f - --> $DIR/target-feature-attr.rs:35:23 + --> $DIR/target-feature-attr.rs:33:23 | LL | asm!("/* {0} */", in(kreg) x); | ^^^^^^^^^^ diff --git a/tests/ui/associated-types/issue-64855-2.rs b/tests/ui/associated-types/issue-64855-2.rs index 30cb37b5198e..20b8ff17e9e5 100644 --- a/tests/ui/associated-types/issue-64855-2.rs +++ b/tests/ui/associated-types/issue-64855-2.rs @@ -1,5 +1,8 @@ -//@ check-pass +// This was originally a test for a `ReEmpty` ICE, but became an unintentional test of +// the coinductiveness of WF predicates. That behavior was removed, and thus this is +// also inadvertently a test for the (non-)co-inductiveness of WF predicates. pub struct Bar<'a>(&'a Self) where Self: ; +//~^ ERROR overflow evaluating the requirement `Bar<'a> well-formed` fn main() {} diff --git a/tests/ui/associated-types/issue-64855-2.stderr b/tests/ui/associated-types/issue-64855-2.stderr new file mode 100644 index 000000000000..22292a8721a1 --- /dev/null +++ b/tests/ui/associated-types/issue-64855-2.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `Bar<'a> well-formed` + --> $DIR/issue-64855-2.rs:5:36 + | +LL | pub struct Bar<'a>(&'a Self) where Self: ; + | ^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-64855-2.rs:5:36 + | +LL | pub struct Bar<'a>(&'a Self) where Self: ; + | ^^^^ required by this bound in `Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/issue-64855.rs b/tests/ui/associated-types/issue-64855.rs index 81cf3ae6e83b..5d325b981a2c 100644 --- a/tests/ui/associated-types/issue-64855.rs +++ b/tests/ui/associated-types/issue-64855.rs @@ -1,8 +1,13 @@ +// This was originally a test for a `ReEmpty` ICE, but became an unintentional test of +// the coinductiveness of WF predicates. That behavior was removed, and thus this is +// also inadvertently a test for the (non-)co-inductiveness of WF predicates. + pub trait Foo { type Type; } pub struct Bar(::Type) where Self: ; //~^ ERROR the trait bound `Bar: Foo` is not satisfied +//~| ERROR overflow evaluating the requirement `Bar well-formed` fn main() {} diff --git a/tests/ui/associated-types/issue-64855.stderr b/tests/ui/associated-types/issue-64855.stderr index 7c09abdb3b6f..d8ba1a9d07ee 100644 --- a/tests/ui/associated-types/issue-64855.stderr +++ b/tests/ui/associated-types/issue-64855.stderr @@ -1,15 +1,28 @@ error[E0277]: the trait bound `Bar: Foo` is not satisfied - --> $DIR/issue-64855.rs:5:19 + --> $DIR/issue-64855.rs:9:19 | LL | pub struct Bar(::Type) where Self: ; | ^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bar` | help: this trait has no implementations, consider adding one - --> $DIR/issue-64855.rs:1:1 + --> $DIR/issue-64855.rs:5:1 | LL | pub trait Foo { | ^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0275]: overflow evaluating the requirement `Bar well-formed` + --> $DIR/issue-64855.rs:9:46 + | +LL | pub struct Bar(::Type) where Self: ; + | ^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-64855.rs:9:46 + | +LL | pub struct Bar(::Type) where Self: ; + | ^^^^ required by this bound in `Bar` -For more information about this error, try `rustc --explain E0277`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0275, E0277. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs new file mode 100644 index 000000000000..1729599f7b3f --- /dev/null +++ b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs @@ -0,0 +1,28 @@ +//@ edition:2021 + +#![feature(async_drop)] +#![allow(incomplete_features)] + +pub struct HasDrop; +impl Drop for HasDrop{ + fn drop(&mut self) { + println!("Sync drop"); + } +} + +pub struct MongoDrop; +impl MongoDrop { + pub async fn new() -> Result { + Ok(Self) + } +} +impl Drop for MongoDrop{ + fn drop(&mut self) { + println!("Sync drop"); + } +} +impl std::future::AsyncDrop for MongoDrop { + async fn drop(self: std::pin::Pin<&mut Self>) { + println!("Async drop"); + } +} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.rs b/tests/ui/async-await/async-drop/dependency-dropped.rs new file mode 100644 index 000000000000..f763bb32b173 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.rs @@ -0,0 +1,34 @@ +//@ run-pass +//@ check-run-results +//@ aux-build:async-drop-dep.rs +//@ edition:2021 + +#![feature(async_drop)] +#![allow(incomplete_features)] + +extern crate async_drop_dep; + +use async_drop_dep::MongoDrop; +use std::pin::pin; +use std::task::{Context, Poll, Waker}; +use std::future::Future; + +async fn asyncdrop() { + let _ = MongoDrop::new().await; +} + +pub fn block_on(fut: impl Future) -> T { + let mut fut = pin!(fut); + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match fut.as_mut().poll(ctx) { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} + +fn main() { + let _ = block_on(asyncdrop()); +} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.run.stdout new file mode 100644 index 000000000000..7aaf70c12d60 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.run.stdout @@ -0,0 +1 @@ +Async drop diff --git a/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs b/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs new file mode 100644 index 000000000000..8d040da1ef7c --- /dev/null +++ b/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs @@ -0,0 +1,14 @@ +struct X { + a: Box, +} + +struct Y { + y: Box, +} + +fn main() { + let a = 8; + let v2 = X { a }; //~ ERROR mismatched types [E0308] + let v3 = Y { y: a }; //~ ERROR mismatched types [E0308] + let v4 = Y { a }; //~ ERROR struct `Y` has no field named `a` [E0560] +} diff --git a/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr new file mode 100644 index 000000000000..01bd0523a162 --- /dev/null +++ b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr @@ -0,0 +1,44 @@ +error[E0308]: mismatched types + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:11:18 + | +LL | let v2 = X { a }; + | ^ expected `Box`, found integer + | + = note: expected struct `Box` + found type `{integer}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | let v2 = X { a: Box::new(a) }; + | ++++++++++++ + + +error[E0308]: mismatched types + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:12:21 + | +LL | let v3 = Y { y: a }; + | ^ expected `Box`, found integer + | + = note: expected struct `Box` + found type `{integer}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | let v3 = Y { y: Box::new(a) }; + | +++++++++ + + +error[E0560]: struct `Y` has no field named `a` + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:13:18 + | +LL | let v4 = Y { a }; + | ^ unknown field + | +help: a field with a similar name exists + | +LL - let v4 = Y { a }; +LL + let v4 = Y { y }; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0560. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs index 9a1ace86e4db..5ec3c7cbdf55 100644 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs @@ -23,7 +23,7 @@ fn main() { let v = 0; &v as *const _ as usize }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs index d1aa95a9a569..731c5b67882b 100644 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs @@ -21,7 +21,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs index f128e1bb0841..94739708ab8b 100644 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs @@ -25,7 +25,7 @@ fn main() { let v = 0; ptr::from_ref(&v).expose_provenance() }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs index 7ccff8d0848e..b7824f53d77f 100644 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs @@ -23,7 +23,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs index 0414879804a5..0f838af1fb19 100644 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs @@ -25,7 +25,7 @@ fn main() { let v = 0; ptr::from_ref(&v).addr() }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs index d963e45e4cdf..20ed991ed3d6 100644 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs @@ -23,7 +23,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/consts/miri_unleashed/abi-mismatch.rs b/tests/ui/consts/miri_unleashed/abi-mismatch.rs index 0a2b3f3abd62..6a46079b39b6 100644 --- a/tests/ui/consts/miri_unleashed/abi-mismatch.rs +++ b/tests/ui/consts/miri_unleashed/abi-mismatch.rs @@ -10,7 +10,7 @@ const fn call_rust_fn(my_fn: extern "Rust" fn()) { static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); //~^ ERROR could not evaluate static initializer -//~| NOTE calling a function with calling convention C using calling convention Rust +//~| NOTE calling a function with calling convention "C" using calling convention "Rust" fn main() {} diff --git a/tests/ui/consts/miri_unleashed/abi-mismatch.stderr b/tests/ui/consts/miri_unleashed/abi-mismatch.stderr index 88623b134b0b..7d1fdcce5261 100644 --- a/tests/ui/consts/miri_unleashed/abi-mismatch.stderr +++ b/tests/ui/consts/miri_unleashed/abi-mismatch.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/abi-mismatch.rs:11:18 | LL | static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using calling convention Rust + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention "C" using calling convention "Rust" | note: inside `call_rust_fn` --> $DIR/abi-mismatch.rs:7:5 diff --git a/tests/ui/coroutine/delayed-obligations-emit.next.stderr b/tests/ui/coroutine/delayed-obligations-emit.next.stderr new file mode 100644 index 000000000000..3a3663398c9a --- /dev/null +++ b/tests/ui/coroutine/delayed-obligations-emit.next.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `{async block@$DIR/delayed-obligations-emit.rs:17:11: 17:16}: Send` + --> $DIR/delayed-obligations-emit.rs:17:5 + | +LL | spawn(async { build_dependencies().await }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `spawn` + --> $DIR/delayed-obligations-emit.rs:31:13 + | +LL | fn spawn(_: F) {} + | ^^^^ required by this bound in `spawn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/coroutine/delayed-obligations-emit.rs b/tests/ui/coroutine/delayed-obligations-emit.rs new file mode 100644 index 000000000000..6334f29fcb22 --- /dev/null +++ b/tests/ui/coroutine/delayed-obligations-emit.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 +//@[current] check-pass + +// This previously caused an ICE with the new solver. +// The delayed coroutine obligations were checked with the +// opaque types inferred by borrowck. +// +// One of these delayed obligations failed with overflow in +// borrowck, causing us to taint `type_of` for the opaque. This +// then caused us to also not emit an error when checking the +// coroutine obligations. + +fn build_multiple<'a>() -> impl Sized { + spawn(async { build_dependencies().await }); + //[next]~^ ERROR overflow evaluating the requirement +} + +// Adding an explicit `Send` bound fixes it. +// Proving `build_dependencies(): Send` in `build_multiple` adds +// addiitional defining uses/placeholders. +fn build_dependencies() -> impl Future /* + Send */ { + async { + Box::pin(build_dependencies()).await; + async { build_multiple() }.await; + } +} + +fn spawn(_: F) {} + +fn main() {} diff --git a/tests/ui/drop/dropck-normalize-errors.rs b/tests/ui/drop/dropck-normalize-errors.rs new file mode 100644 index 000000000000..793122bd33d4 --- /dev/null +++ b/tests/ui/drop/dropck-normalize-errors.rs @@ -0,0 +1,31 @@ +// Test that we don't ICE when computing the drop types for + +trait Decode<'a> { + type Decoder; +} + +trait NonImplementedTrait { + type Assoc; +} +struct NonImplementedStruct; + +pub struct ADecoder<'a> { + b: >::Decoder, +} +fn make_a_decoder<'a>() -> ADecoder<'a> { + //~^ ERROR the trait bound + //~| ERROR the trait bound + panic!() +} + +struct B; +impl<'a> Decode<'a> for B { + type Decoder = BDecoder; + //~^ ERROR the trait bound +} +pub struct BDecoder { + non_implemented: ::Assoc, + //~^ ERROR the trait bound +} + +fn main() {} diff --git a/tests/ui/drop/dropck-normalize-errors.stderr b/tests/ui/drop/dropck-normalize-errors.stderr new file mode 100644 index 000000000000..2bb5909c6b22 --- /dev/null +++ b/tests/ui/drop/dropck-normalize-errors.stderr @@ -0,0 +1,76 @@ +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:15:28 + | +LL | fn make_a_decoder<'a>() -> ADecoder<'a> { + | ^^^^^^^^^^^^ within `ADecoder<'a>`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:26:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required because it appears within the type `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:12:12 + | +LL | pub struct ADecoder<'a> { + | ^^^^^^^^ + = note: the return type of a function must have a statically known size + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `BDecoder` + --> $DIR/dropck-normalize-errors.rs:23:20 + | +LL | type Decoder = BDecoder; + | ^^^^^^^^ within `BDecoder`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:26:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required by a bound in `Decode::Decoder` + --> $DIR/dropck-normalize-errors.rs:4:5 + | +LL | type Decoder; + | ^^^^^^^^^^^^^ required by this bound in `Decode::Decoder` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Decoder: ?Sized; + | ++++++++ + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied + --> $DIR/dropck-normalize-errors.rs:27:22 + | +LL | non_implemented: ::Assoc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied + --> $DIR/dropck-normalize-errors.rs:15:28 + | +LL | fn make_a_decoder<'a>() -> ADecoder<'a> { + | ^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr index 12d7f5b6cd30..28d7546d0c9b 100644 --- a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr +++ b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr @@ -14,6 +14,22 @@ help: consider further restricting type parameter `T` with trait `Copy` LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` | ++++++++++++++++++++ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:32:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider further restricting type parameter `T` with trait `Copy` + | +LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` + | ++++++++++++++++++++ + error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/explicit-drop-bounds.rs:32:18 | @@ -30,6 +46,6 @@ help: consider further restricting type parameter `T` with trait `Copy` LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` | ++++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.bad2.stderr b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr index 5851731e8346..c363676edea3 100644 --- a/tests/ui/dropck/explicit-drop-bounds.bad2.stderr +++ b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/explicit-drop-bounds.rs:37:18 + --> $DIR/explicit-drop-bounds.rs:38:18 | LL | impl Drop for DropMe | ^^^^^^^^^ the trait `Copy` is not implemented for `T` @@ -15,7 +15,23 @@ LL | impl Drop for DropMe | +++++++++++++++++++ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/explicit-drop-bounds.rs:40:18 + --> $DIR/explicit-drop-bounds.rs:41:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider restricting type parameter `T` with trait `Copy` + | +LL | impl Drop for DropMe + | +++++++++++++++++++ + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:41:18 | LL | fn drop(&mut self) {} | ^^^^ the trait `Copy` is not implemented for `T` @@ -30,6 +46,6 @@ help: consider restricting type parameter `T` with trait `Copy` LL | impl Drop for DropMe | +++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.rs b/tests/ui/dropck/explicit-drop-bounds.rs index 6ddac4d314f4..cd1d89ed9dbd 100644 --- a/tests/ui/dropck/explicit-drop-bounds.rs +++ b/tests/ui/dropck/explicit-drop-bounds.rs @@ -31,6 +31,7 @@ where { fn drop(&mut self) {} //[bad1]~^ ERROR the trait bound `T: Copy` is not satisfied + //[bad1]~| ERROR the trait bound `T: Copy` is not satisfied } #[cfg(bad2)] @@ -39,6 +40,7 @@ impl Drop for DropMe { fn drop(&mut self) {} //[bad2]~^ ERROR the trait bound `T: Copy` is not satisfied + //[bad2]~| ERROR the trait bound `T: Copy` is not satisfied } fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs index 74fb5817081c..095f66eeb906 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -22,7 +22,6 @@ fn other_guards_dont() { let ((x if guard(x)) | x) = 0; //~^ ERROR: guard patterns are experimental - //~| ERROR: cannot find value `x` if let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental @@ -37,7 +36,6 @@ fn other_guards_dont() { fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} //~^ ERROR: guard patterns are experimental -//~| ERROR: cannot find value `x` fn guard(x: T) -> bool { unimplemented!() diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr index 8b85b663889f..b0bf302f3cb6 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -10,24 +10,6 @@ LL - (0 if guard(0)) => {}, LL + 0 if guard(0) => {}, | -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:23:22 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ not found in this scope - -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:38:45 - | -LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} - | ^ - | -help: the binding `x` is available in a different scope in the same function - --> $DIR/feature-gate-guard-patterns.rs:23:11 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ - error[E0658]: guard patterns are experimental --> $DIR/feature-gate-guard-patterns.rs:18:15 | @@ -51,7 +33,7 @@ LL | let ((x if guard(x)) | x) = 0; = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:27:18 + --> $DIR/feature-gate-guard-patterns.rs:26:18 | LL | if let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -62,7 +44,7 @@ LL | if let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:30:21 + --> $DIR/feature-gate-guard-patterns.rs:29:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -73,7 +55,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:34:21 + --> $DIR/feature-gate-guard-patterns.rs:33:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -84,7 +66,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:38:39 + --> $DIR/feature-gate-guard-patterns.rs:37:39 | LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} | ^^^^^^^^ @@ -94,7 +76,6 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider using match arm guards -error: aborting due to 9 previous errors +error: aborting due to 7 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 E0658`. diff --git a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs index 96a0f2f40bf4..82ffa0221b99 100644 --- a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs +++ b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs @@ -1,5 +1,9 @@ -// Fix for . //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Fix for . trait Tr { type Gat; diff --git a/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs new file mode 100644 index 000000000000..0c25c64224b1 --- /dev/null +++ b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#202. We have +// to make sure we don't constrain ambiguous GAT args when normalizing +// via where bounds or item bounds. + +trait Trait { + type Assoc; +} + +fn ret(x: U) -> ::Assoc { + loop {} +} + +fn where_bound = u32>>() { + let inf = Default::default(); + let x = ret::(inf); + let _: i32 = inf; +} + +trait ItemBound { + type Bound: Trait = u32>; +} +fn item_bound() { + let inf = Default::default(); + let x = ret::(inf); + let _: i32 = inf; +} + +fn main() {} diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95230.rs b/tests/ui/higher-ranked/trait-bounds/issue-95230.rs index d1ca6834551e..821a04ff0655 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-95230.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-95230.rs @@ -1,11 +1,10 @@ -//@ revisions: old next -//@[next] compile-flags: -Znext-solver -//@[old] check-pass -//@[next] known-bug: #109764 - +// This used to be a test for overflow handling + higher-ranked outlives +// in the new solver, but this test isn't expected to pass since WF preds +// are not coinductive anymore. pub struct Bar where for<'a> &'a mut Self:; +//~^ ERROR overflow evaluating the requirement `for<'a> &'a mut Bar well-formed` fn main() {} diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr b/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr new file mode 100644 index 000000000000..7070af75d290 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr @@ -0,0 +1,18 @@ +error[E0275]: overflow evaluating the requirement `for<'a> &'a mut Bar well-formed` + --> $DIR/issue-95230.rs:7:13 + | +LL | for<'a> &'a mut Self:; + | ^^^^^^^^^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-95230.rs:7:13 + | +LL | pub struct Bar + | --- required by a bound in this struct +LL | where +LL | for<'a> &'a mut Self:; + | ^^^^^^^^^^^^ required by this bound in `Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/impl-trait/arg-position-impl-trait-too-long.rs b/tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.rs similarity index 100% rename from tests/ui/impl-trait/arg-position-impl-trait-too-long.rs rename to tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.rs diff --git a/tests/ui/impl-trait/arg-position-impl-trait-too-long.stderr b/tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.stderr similarity index 100% rename from tests/ui/impl-trait/arg-position-impl-trait-too-long.stderr rename to tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.stderr diff --git a/tests/ui/impl-trait/impl-generic-mismatch-ab.rs b/tests/ui/impl-trait/apit/impl-generic-mismatch-ab.rs similarity index 100% rename from tests/ui/impl-trait/impl-generic-mismatch-ab.rs rename to tests/ui/impl-trait/apit/impl-generic-mismatch-ab.rs diff --git a/tests/ui/impl-trait/impl-generic-mismatch-ab.stderr b/tests/ui/impl-trait/apit/impl-generic-mismatch-ab.stderr similarity index 100% rename from tests/ui/impl-trait/impl-generic-mismatch-ab.stderr rename to tests/ui/impl-trait/apit/impl-generic-mismatch-ab.stderr diff --git a/tests/ui/impl-trait/can-return-unconstrained-closure.rs b/tests/ui/impl-trait/can-return-unconstrained-closure.rs deleted file mode 100644 index 1f8bdbc5054d..000000000000 --- a/tests/ui/impl-trait/can-return-unconstrained-closure.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Test that we are special casing "outlives" for opaque types. -// -// The return type of a closure is not required to outlive the closure. As such -// the following code would not compile if we used a standard outlives check -// when checking the return type, because the return type of the closure would -// be `&ReEmpty i32`, and we don't allow `ReEmpty` to occur in the concrete -// type used for an opaque type. -// -// However, opaque types are special cased to include check all regions in the -// concrete type against the bound, which forces the return type to be -// `&'static i32` here. - -//@ build-pass (FIXME(62277): could be check-pass?) - -fn make_identity() -> impl Sized { - |x: &'static i32| x -} - -fn make_identity_static() -> impl Sized + 'static { - |x: &'static i32| x -} - -fn main() {} diff --git a/tests/ui/impl-trait/issue-100075-2.rs b/tests/ui/impl-trait/issues/issue-100075-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-100075-2.rs rename to tests/ui/impl-trait/issues/issue-100075-2.rs diff --git a/tests/ui/impl-trait/issue-100075-2.stderr b/tests/ui/impl-trait/issues/issue-100075-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-100075-2.stderr rename to tests/ui/impl-trait/issues/issue-100075-2.stderr diff --git a/tests/ui/impl-trait/issue-100075.rs b/tests/ui/impl-trait/issues/issue-100075.rs similarity index 100% rename from tests/ui/impl-trait/issue-100075.rs rename to tests/ui/impl-trait/issues/issue-100075.rs diff --git a/tests/ui/impl-trait/issue-100075.stderr b/tests/ui/impl-trait/issues/issue-100075.stderr similarity index 100% rename from tests/ui/impl-trait/issue-100075.stderr rename to tests/ui/impl-trait/issues/issue-100075.stderr diff --git a/tests/ui/impl-trait/issue-100187.rs b/tests/ui/impl-trait/issues/issue-100187.rs similarity index 100% rename from tests/ui/impl-trait/issue-100187.rs rename to tests/ui/impl-trait/issues/issue-100187.rs diff --git a/tests/ui/impl-trait/issue-102605.rs b/tests/ui/impl-trait/issues/issue-102605.rs similarity index 100% rename from tests/ui/impl-trait/issue-102605.rs rename to tests/ui/impl-trait/issues/issue-102605.rs diff --git a/tests/ui/impl-trait/issue-102605.stderr b/tests/ui/impl-trait/issues/issue-102605.stderr similarity index 100% rename from tests/ui/impl-trait/issue-102605.stderr rename to tests/ui/impl-trait/issues/issue-102605.stderr diff --git a/tests/ui/impl-trait/issue-103181-1.current.stderr b/tests/ui/impl-trait/issues/issue-103181-1.current.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103181-1.current.stderr rename to tests/ui/impl-trait/issues/issue-103181-1.current.stderr diff --git a/tests/ui/impl-trait/issue-103181-1.next.stderr b/tests/ui/impl-trait/issues/issue-103181-1.next.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103181-1.next.stderr rename to tests/ui/impl-trait/issues/issue-103181-1.next.stderr diff --git a/tests/ui/impl-trait/issue-103181-1.rs b/tests/ui/impl-trait/issues/issue-103181-1.rs similarity index 100% rename from tests/ui/impl-trait/issue-103181-1.rs rename to tests/ui/impl-trait/issues/issue-103181-1.rs diff --git a/tests/ui/impl-trait/issue-103181-2.rs b/tests/ui/impl-trait/issues/issue-103181-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-103181-2.rs rename to tests/ui/impl-trait/issues/issue-103181-2.rs diff --git a/tests/ui/impl-trait/issue-103181-2.stderr b/tests/ui/impl-trait/issues/issue-103181-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103181-2.stderr rename to tests/ui/impl-trait/issues/issue-103181-2.stderr diff --git a/tests/ui/impl-trait/issue-103599.rs b/tests/ui/impl-trait/issues/issue-103599.rs similarity index 100% rename from tests/ui/impl-trait/issue-103599.rs rename to tests/ui/impl-trait/issues/issue-103599.rs diff --git a/tests/ui/impl-trait/issue-103599.stderr b/tests/ui/impl-trait/issues/issue-103599.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103599.stderr rename to tests/ui/impl-trait/issues/issue-103599.stderr diff --git a/tests/ui/impl-trait/issue-108591.rs b/tests/ui/impl-trait/issues/issue-108591.rs similarity index 100% rename from tests/ui/impl-trait/issue-108591.rs rename to tests/ui/impl-trait/issues/issue-108591.rs diff --git a/tests/ui/impl-trait/issue-108592.rs b/tests/ui/impl-trait/issues/issue-108592.rs similarity index 100% rename from tests/ui/impl-trait/issue-108592.rs rename to tests/ui/impl-trait/issues/issue-108592.rs diff --git a/tests/ui/impl-trait/issue-35668.rs b/tests/ui/impl-trait/issues/issue-35668.rs similarity index 100% rename from tests/ui/impl-trait/issue-35668.rs rename to tests/ui/impl-trait/issues/issue-35668.rs diff --git a/tests/ui/impl-trait/issue-35668.stderr b/tests/ui/impl-trait/issues/issue-35668.stderr similarity index 100% rename from tests/ui/impl-trait/issue-35668.stderr rename to tests/ui/impl-trait/issues/issue-35668.stderr diff --git a/tests/ui/impl-trait/issue-36792.rs b/tests/ui/impl-trait/issues/issue-36792.rs similarity index 100% rename from tests/ui/impl-trait/issue-36792.rs rename to tests/ui/impl-trait/issues/issue-36792.rs diff --git a/tests/ui/impl-trait/issue-46959.rs b/tests/ui/impl-trait/issues/issue-46959.rs similarity index 100% rename from tests/ui/impl-trait/issue-46959.rs rename to tests/ui/impl-trait/issues/issue-46959.rs diff --git a/tests/ui/impl-trait/issue-49556.rs b/tests/ui/impl-trait/issues/issue-49556.rs similarity index 100% rename from tests/ui/impl-trait/issue-49556.rs rename to tests/ui/impl-trait/issues/issue-49556.rs diff --git a/tests/ui/impl-trait/issue-49579.rs b/tests/ui/impl-trait/issues/issue-49579.rs similarity index 100% rename from tests/ui/impl-trait/issue-49579.rs rename to tests/ui/impl-trait/issues/issue-49579.rs diff --git a/tests/ui/impl-trait/issue-49685.rs b/tests/ui/impl-trait/issues/issue-49685.rs similarity index 100% rename from tests/ui/impl-trait/issue-49685.rs rename to tests/ui/impl-trait/issues/issue-49685.rs diff --git a/tests/ui/impl-trait/issue-51185.rs b/tests/ui/impl-trait/issues/issue-51185.rs similarity index 100% rename from tests/ui/impl-trait/issue-51185.rs rename to tests/ui/impl-trait/issues/issue-51185.rs diff --git a/tests/ui/impl-trait/issue-54966.rs b/tests/ui/impl-trait/issues/issue-54966.rs similarity index 100% rename from tests/ui/impl-trait/issue-54966.rs rename to tests/ui/impl-trait/issues/issue-54966.rs diff --git a/tests/ui/impl-trait/issue-54966.stderr b/tests/ui/impl-trait/issues/issue-54966.stderr similarity index 100% rename from tests/ui/impl-trait/issue-54966.stderr rename to tests/ui/impl-trait/issues/issue-54966.stderr diff --git a/tests/ui/impl-trait/issue-55872-1.rs b/tests/ui/impl-trait/issues/issue-55872-1.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872-1.rs rename to tests/ui/impl-trait/issues/issue-55872-1.rs diff --git a/tests/ui/impl-trait/issue-55872-1.stderr b/tests/ui/impl-trait/issues/issue-55872-1.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872-1.stderr rename to tests/ui/impl-trait/issues/issue-55872-1.stderr diff --git a/tests/ui/impl-trait/issue-55872-2.rs b/tests/ui/impl-trait/issues/issue-55872-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872-2.rs rename to tests/ui/impl-trait/issues/issue-55872-2.rs diff --git a/tests/ui/impl-trait/issue-55872-2.stderr b/tests/ui/impl-trait/issues/issue-55872-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872-2.stderr rename to tests/ui/impl-trait/issues/issue-55872-2.stderr diff --git a/tests/ui/impl-trait/issue-55872-3.rs b/tests/ui/impl-trait/issues/issue-55872-3.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872-3.rs rename to tests/ui/impl-trait/issues/issue-55872-3.rs diff --git a/tests/ui/impl-trait/issue-55872-3.stderr b/tests/ui/impl-trait/issues/issue-55872-3.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872-3.stderr rename to tests/ui/impl-trait/issues/issue-55872-3.stderr diff --git a/tests/ui/impl-trait/issue-55872.rs b/tests/ui/impl-trait/issues/issue-55872.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872.rs rename to tests/ui/impl-trait/issues/issue-55872.rs diff --git a/tests/ui/impl-trait/issue-55872.stderr b/tests/ui/impl-trait/issues/issue-55872.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872.stderr rename to tests/ui/impl-trait/issues/issue-55872.stderr diff --git a/tests/ui/impl-trait/issue-56445.rs b/tests/ui/impl-trait/issues/issue-56445.rs similarity index 100% rename from tests/ui/impl-trait/issue-56445.rs rename to tests/ui/impl-trait/issues/issue-56445.rs diff --git a/tests/ui/impl-trait/issue-68532.rs b/tests/ui/impl-trait/issues/issue-68532.rs similarity index 100% rename from tests/ui/impl-trait/issue-68532.rs rename to tests/ui/impl-trait/issues/issue-68532.rs diff --git a/tests/ui/impl-trait/issue-72911.rs b/tests/ui/impl-trait/issues/issue-72911.rs similarity index 100% rename from tests/ui/impl-trait/issue-72911.rs rename to tests/ui/impl-trait/issues/issue-72911.rs diff --git a/tests/ui/impl-trait/issue-72911.stderr b/tests/ui/impl-trait/issues/issue-72911.stderr similarity index 100% rename from tests/ui/impl-trait/issue-72911.stderr rename to tests/ui/impl-trait/issues/issue-72911.stderr diff --git a/tests/ui/impl-trait/issue-87450.rs b/tests/ui/impl-trait/issues/issue-87450.rs similarity index 100% rename from tests/ui/impl-trait/issue-87450.rs rename to tests/ui/impl-trait/issues/issue-87450.rs diff --git a/tests/ui/impl-trait/issue-87450.stderr b/tests/ui/impl-trait/issues/issue-87450.stderr similarity index 100% rename from tests/ui/impl-trait/issue-87450.stderr rename to tests/ui/impl-trait/issues/issue-87450.stderr diff --git a/tests/ui/impl-trait/issue-99073-2.rs b/tests/ui/impl-trait/issues/issue-99073-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-99073-2.rs rename to tests/ui/impl-trait/issues/issue-99073-2.rs diff --git a/tests/ui/impl-trait/issue-99073-2.stderr b/tests/ui/impl-trait/issues/issue-99073-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-99073-2.stderr rename to tests/ui/impl-trait/issues/issue-99073-2.stderr diff --git a/tests/ui/impl-trait/issue-99073.rs b/tests/ui/impl-trait/issues/issue-99073.rs similarity index 100% rename from tests/ui/impl-trait/issue-99073.rs rename to tests/ui/impl-trait/issues/issue-99073.rs diff --git a/tests/ui/impl-trait/issue-99073.stderr b/tests/ui/impl-trait/issues/issue-99073.stderr similarity index 100% rename from tests/ui/impl-trait/issue-99073.stderr rename to tests/ui/impl-trait/issues/issue-99073.stderr diff --git a/tests/ui/impl-trait/issue-99642-2.rs b/tests/ui/impl-trait/issues/issue-99642-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-99642-2.rs rename to tests/ui/impl-trait/issues/issue-99642-2.rs diff --git a/tests/ui/impl-trait/issue-99642.rs b/tests/ui/impl-trait/issues/issue-99642.rs similarity index 100% rename from tests/ui/impl-trait/issue-99642.rs rename to tests/ui/impl-trait/issues/issue-99642.rs diff --git a/tests/ui/impl-trait/issue-99914.rs b/tests/ui/impl-trait/issues/issue-99914.rs similarity index 100% rename from tests/ui/impl-trait/issue-99914.rs rename to tests/ui/impl-trait/issues/issue-99914.rs diff --git a/tests/ui/impl-trait/issue-99914.stderr b/tests/ui/impl-trait/issues/issue-99914.stderr similarity index 100% rename from tests/ui/impl-trait/issue-99914.stderr rename to tests/ui/impl-trait/issues/issue-99914.stderr diff --git a/tests/ui/impl-trait/lifetimes2.rs b/tests/ui/impl-trait/lifetimes2.rs deleted file mode 100644 index facf2f75bc4d..000000000000 --- a/tests/ui/impl-trait/lifetimes2.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ check-pass - -pub fn keys<'a>(x: &'a Result) -> impl std::fmt::Debug + 'a { - match x { - Ok(map) => Ok(map), - Err(map) => Err(map), - } -} - -fn main() {} diff --git a/tests/ui/imports/issue-99695-b.fixed b/tests/ui/imports/issue-99695-b.fixed index 0108f762400f..ae63b0c4627c 100644 --- a/tests/ui/imports/issue-99695-b.fixed +++ b/tests/ui/imports/issue-99695-b.fixed @@ -11,7 +11,7 @@ mod m { pub struct other_item; } - use ::nu; + use crate::nu; pub use self::p::{other_item as _}; //~^ ERROR unresolved import `self::p::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate diff --git a/tests/ui/imports/issue-99695-b.stderr b/tests/ui/imports/issue-99695-b.stderr index d58d2798746c..ad752d5c45a7 100644 --- a/tests/ui/imports/issue-99695-b.stderr +++ b/tests/ui/imports/issue-99695-b.stderr @@ -7,7 +7,7 @@ LL | pub use self::p::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use ::nu; +LL ~ use crate::nu; LL ~ pub use self::p::{other_item as _}; | diff --git a/tests/ui/imports/issue-99695.fixed b/tests/ui/imports/issue-99695.edition_2015.fixed similarity index 71% rename from tests/ui/imports/issue-99695.fixed rename to tests/ui/imports/issue-99695.edition_2015.fixed index 51ccd3f8c48d..798acfd58746 100644 --- a/tests/ui/imports/issue-99695.fixed +++ b/tests/ui/imports/issue-99695.edition_2015.fixed @@ -1,4 +1,8 @@ //@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + #![allow(unused, nonstandard_style)] mod m { #[macro_export] @@ -8,7 +12,7 @@ mod m { pub struct other_item; - use ::nu; + use crate::nu; pub use self::{other_item as _}; //~^ ERROR unresolved import `self::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate diff --git a/tests/ui/imports/issue-99695.stderr b/tests/ui/imports/issue-99695.edition_2015.stderr similarity index 90% rename from tests/ui/imports/issue-99695.stderr rename to tests/ui/imports/issue-99695.edition_2015.stderr index 536f51dcb3b2..4ef8e6426fb2 100644 --- a/tests/ui/imports/issue-99695.stderr +++ b/tests/ui/imports/issue-99695.edition_2015.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `self::nu` - --> $DIR/issue-99695.rs:11:20 + --> $DIR/issue-99695.rs:15:20 | LL | pub use self::{nu, other_item as _}; | ^^ no `nu` in `m` @@ -7,7 +7,7 @@ LL | pub use self::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use ::nu; +LL ~ use crate::nu; LL ~ pub use self::{other_item as _}; | diff --git a/tests/ui/imports/issue-99695.edition_2018.fixed b/tests/ui/imports/issue-99695.edition_2018.fixed new file mode 100644 index 000000000000..798acfd58746 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2018.fixed @@ -0,0 +1,21 @@ +//@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + +#![allow(unused, nonstandard_style)] +mod m { + #[macro_export] + macro_rules! nu { + {} => {}; + } + + pub struct other_item; + + use crate::nu; +pub use self::{other_item as _}; + //~^ ERROR unresolved import `self::nu` [E0432] + //~| HELP a macro with this name exists at the root of the crate +} + +fn main() {} diff --git a/tests/ui/imports/issue-99695.edition_2018.stderr b/tests/ui/imports/issue-99695.edition_2018.stderr new file mode 100644 index 000000000000..4ef8e6426fb2 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2018.stderr @@ -0,0 +1,16 @@ +error[E0432]: unresolved import `self::nu` + --> $DIR/issue-99695.rs:15:20 + | +LL | pub use self::{nu, other_item as _}; + | ^^ no `nu` in `m` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ use crate::nu; +LL ~ pub use self::{other_item as _}; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/issue-99695.rs b/tests/ui/imports/issue-99695.rs index a52370e0eb08..dc0a8f438e00 100644 --- a/tests/ui/imports/issue-99695.rs +++ b/tests/ui/imports/issue-99695.rs @@ -1,4 +1,8 @@ //@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + #![allow(unused, nonstandard_style)] mod m { #[macro_export] diff --git a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs index 6380449124ff..3b297a9a6629 100644 --- a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs +++ b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs @@ -30,7 +30,8 @@ where type Output = B; extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument - self.call_mut(a) + //~| ERROR type parameter to bare `FnOnce` trait must be a tuple + self.call_mut(a) //~^ ERROR `A` is not a tuple } } @@ -43,6 +44,7 @@ where { extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + //~| ERROR type parameter to bare `FnOnce` trait must be a tuple self.cache.get(&a).map(|a| a.clone()).unwrap_or_else(|| { let b = (self.fun)(self, a.clone()); self.cache.insert(a, b.clone()); diff --git a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr index 3b051ef9a882..32a564e466be 100644 --- a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr +++ b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr @@ -11,8 +11,21 @@ help: consider further restricting type parameter `A` with unstable trait `Tuple LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ +error[E0059]: type parameter to bare `FnOnce` trait must be a tuple + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 + | +LL | extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | +note: required by a bound in `FnOnce` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL +help: consider further restricting type parameter `A` with unstable trait `Tuple` + | +LL | A: Eq + Hash + Clone + std::marker::Tuple, + | ++++++++++++++++++++ + error[E0059]: type parameter to bare `FnMut` trait must be a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:38:12 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:39:12 | LL | impl FnMut for CachedFun | ^^^^^^^^ the trait `Tuple` is not implemented for `A` @@ -24,6 +37,19 @@ help: consider further restricting type parameter `A` with unstable trait `Tuple LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ +error[E0059]: type parameter to bare `FnOnce` trait must be a tuple + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 + | +LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | +note: required by a bound in `FnOnce` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL +help: consider further restricting type parameter `A` with unstable trait `Tuple` + | +LL | A: Eq + Hash + Clone + std::marker::Tuple, + | ++++++++++++++++++++ + error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 | @@ -36,7 +62,7 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:44:5 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 | LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` @@ -47,12 +73,12 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: `A` is not a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:33:23 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:34:19 | -LL | self.call_mut(a) - | -------- ^ the trait `Tuple` is not implemented for `A` - | | - | required by a bound introduced by this call +LL | self.call_mut(a) + | -------- ^ the trait `Tuple` is not implemented for `A` + | | + | required by a bound introduced by this call | note: required by a bound in `call_mut` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -62,7 +88,7 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: `i32` is not a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:57:26 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:59:26 | LL | cachedcoso.call_once(1); | --------- ^ the trait `Tuple` is not implemented for `i32` @@ -76,7 +102,7 @@ help: use a unary tuple instead LL | cachedcoso.call_once((1,)); | + ++ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0059, E0277. For more information about an error, try `rustc --explain E0059`. diff --git a/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr b/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr new file mode 100644 index 000000000000..28e2c74fda22 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr @@ -0,0 +1,2 @@ +error: incorrect value `*lld` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.no_value.stderr b/tests/ui/linking/link-self-contained-malformed.no_value.stderr new file mode 100644 index 000000000000..dd8e8af074bf --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.no_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.rs b/tests/ui/linking/link-self-contained-malformed.rs new file mode 100644 index 000000000000..8ccb82eee270 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.rs @@ -0,0 +1,23 @@ +//! Check that malformed `-Clink-self-contained` invocations are properly rejected. + +//@ revisions: no_value +//@[no_value] compile-flags: -Clink-self-contained= +//[no_value]~? ERROR incorrect value `` for codegen option `link-self-contained` + +//@ revisions: invalid_modifier +//@[invalid_modifier] compile-flags: -Clink-self-contained=*lld +//[invalid_modifier]~? ERROR incorrect value `*lld` for codegen option `link-self-contained` + +//@ revisions: unknown_value +//@[unknown_value] compile-flags: -Clink-self-contained=unknown +//[unknown_value]~? ERROR incorrect value `unknown` for codegen option `link-self-contained` + +//@ revisions: unknown_modifier_value +//@[unknown_modifier_value] compile-flags: -Clink-self-contained=-unknown +//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for codegen option `link-self-contained` + +//@ revisions: unknown_boolean +//@[unknown_boolean] compile-flags: -Clink-self-contained=maybe +//[unknown_boolean]~? ERROR incorrect value `maybe` for codegen option `link-self-contained` + +fn main() {} diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr new file mode 100644 index 000000000000..7924074d1bf4 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr @@ -0,0 +1,2 @@ +error: incorrect value `maybe` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr new file mode 100644 index 000000000000..2dc58c0f7e8f --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-unknown` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr new file mode 100644 index 000000000000..ce4c44299cd2 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `unknown` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr new file mode 100644 index 000000000000..909b277089f4 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr @@ -0,0 +1,2 @@ +error: incorrect value `*lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.invalid_separator.stderr b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr new file mode 100644 index 000000000000..0f84898a7742 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-lld@+lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.no_value.stderr b/tests/ui/linking/linker-features-malformed.no_value.stderr new file mode 100644 index 000000000000..e93a4e79bb1d --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.no_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.rs b/tests/ui/linking/linker-features-malformed.rs new file mode 100644 index 000000000000..0bdcfa39920f --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.rs @@ -0,0 +1,27 @@ +//! Check that malformed `-Zlinker-features` flags are properly rejected. + +//@ revisions: no_value +//@[no_value] compile-flags: -Zlinker-features= +//[no_value]~? ERROR incorrect value `` for unstable option `linker-features` + +//@ revisions: invalid_modifier +//@[invalid_modifier] compile-flags: -Zlinker-features=*lld +//[invalid_modifier]~? ERROR incorrect value `*lld` for unstable option `linker-features` + +//@ revisions: unknown_value +//@[unknown_value] compile-flags: -Zlinker-features=unknown +//[unknown_value]~? ERROR incorrect value `unknown` for unstable option `linker-features` + +//@ revisions: unknown_modifier_value +//@[unknown_modifier_value] compile-flags: -Zlinker-features=-unknown +//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for unstable option `linker-features` + +//@ revisions: unknown_boolean +//@[unknown_boolean] compile-flags: -Zlinker-features=maybe +//[unknown_boolean]~? ERROR incorrect value `maybe` for unstable option `linker-features` + +//@ revisions: invalid_separator +//@[invalid_separator] compile-flags: -Zlinker-features=-lld@+lld +//[invalid_separator]~? ERROR incorrect value `-lld@+lld` for unstable option `linker-features` + +fn main() {} diff --git a/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr new file mode 100644 index 000000000000..865738d0ccc1 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr @@ -0,0 +1,2 @@ +error: incorrect value `maybe` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr new file mode 100644 index 000000000000..03b9620ca263 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.unknown_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_value.stderr new file mode 100644 index 000000000000..566632a3df38 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/lint/implicit_autorefs.fixed b/tests/ui/lint/implicit_autorefs.fixed index 96a617b20c91..454dfe763726 100644 --- a/tests/ui/lint/implicit_autorefs.fixed +++ b/tests/ui/lint/implicit_autorefs.fixed @@ -96,4 +96,10 @@ unsafe fn test_string(ptr: *mut String) { //~^ WARN implicit autoref } +unsafe fn slice_ptr_len_because_of_msrv(slice: *const [T]) { + let _ = (&(&(*slice))[..]).len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.rs b/tests/ui/lint/implicit_autorefs.rs index 61dd0ac50ce7..507d65368283 100644 --- a/tests/ui/lint/implicit_autorefs.rs +++ b/tests/ui/lint/implicit_autorefs.rs @@ -96,4 +96,10 @@ unsafe fn test_string(ptr: *mut String) { //~^ WARN implicit autoref } +unsafe fn slice_ptr_len_because_of_msrv(slice: *const [T]) { + let _ = (*slice)[..].len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.stderr b/tests/ui/lint/implicit_autorefs.stderr index 6dd1ac65ada9..80ba8ae2fd27 100644 --- a/tests/ui/lint/implicit_autorefs.stderr +++ b/tests/ui/lint/implicit_autorefs.stderr @@ -2,9 +2,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:10:13 | LL | let _ = (*ptr)[..16]; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const [u8]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:10:13 + | +LL | let _ = (*ptr)[..16]; + | ^^^^^^ = note: `#[warn(dangerous_implicit_autorefs)]` on by default help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | @@ -15,9 +22,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:19:13 | LL | let l = (*ptr).field.len(); - | ^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:19:13 + | +LL | let l = (*ptr).field.len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let l = (&(*ptr).field).len(); @@ -27,9 +43,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:22:16 | LL | &raw const (*ptr).field[..l - 1] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:22:16 + | +LL | &raw const (*ptr).field[..l - 1] + | ^^^^^^^^^^^^ help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | &raw const (&(*ptr).field)[..l - 1] @@ -39,9 +62,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:27:9 | LL | _ = (*a)[0].len(); - | ^^^^^^^^^^^^^ + | ^^-^^^^^^^^^^ + | | + | this raw pointer has type `*mut [String]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:27:9 + | +LL | _ = (*a)[0].len(); + | ^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | _ = (&(*a)[0]).len(); @@ -51,9 +83,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:30:9 | LL | _ = (*a)[..1][0].len(); - | ^^^^^^^^^^^^^^^^^^ + | ^^-^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut [String]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | _ = (&(*a)[..1][0]).len(); @@ -63,9 +104,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:30:9 | LL | _ = (*a)[..1][0].len(); - | ^^^^^^^^^ + | ^^-^^^^^^ + | | + | this raw pointer has type `*mut [String]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[String]` + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^ help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | _ = (&(*a))[..1][0].len(); @@ -75,9 +123,12 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:36:13 | LL | let _ = (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -87,9 +138,12 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:38:24 | LL | let _ = &raw const (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = &raw const (&(*ptr)).field; @@ -99,9 +153,12 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:43:13 | LL | let _ = (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*mut ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -111,9 +168,12 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:48:13 | LL | let _ = (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop>` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -123,9 +183,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:62:26 | LL | let _p: *const i32 = &raw const **w; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^- + | | + | this raw pointer has type `*const W` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&W` + --> $DIR/implicit_autorefs.rs:62:38 + | +LL | let _p: *const i32 = &raw const **w; + | ^^ help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _p: *const i32 = &raw const *(&**w); @@ -135,9 +202,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:72:14 | LL | unsafe { (*ptr).field.len() } - | ^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test2` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:72:14 + | +LL | unsafe { (*ptr).field.len() } + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | unsafe { (&(*ptr).field).len() } @@ -147,9 +223,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:82:13 | LL | let _ = (*ptr).get(0); - | ^^^^^^^^^^^^^ + | ^^---^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:82:13 + | +LL | let _ = (*ptr).get(0); + | ^^^^^^ +note: method calls to `get` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).get(0); @@ -159,9 +244,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:84:13 | LL | let _ = (*ptr).get_unchecked(0); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:84:13 + | +LL | let _ = (*ptr).get_unchecked(0); + | ^^^^^^ +note: method calls to `get_unchecked` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).get_unchecked(0); @@ -171,9 +265,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:86:13 | LL | let _ = (*ptr).get_mut(0); - | ^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&mut [u8]` + --> $DIR/implicit_autorefs.rs:86:13 + | +LL | let _ = (*ptr).get_mut(0); + | ^^^^^^ +note: method calls to `get_mut` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&mut (*ptr)).get_mut(0); @@ -183,9 +286,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:88:13 | LL | let _ = (*ptr).get_unchecked_mut(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&mut [u8]` + --> $DIR/implicit_autorefs.rs:88:13 + | +LL | let _ = (*ptr).get_unchecked_mut(0); + | ^^^^^^ +note: method calls to `get_unchecked_mut` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&mut (*ptr)).get_unchecked_mut(0); @@ -195,9 +307,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:93:13 | LL | let _ = (*ptr).len(); - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*mut String` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:93:13 + | +LL | let _ = (*ptr).len(); + | ^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).len(); @@ -207,13 +328,62 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:95:13 | LL | let _ = (*ptr).is_empty(); - | ^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut String` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:95:13 + | +LL | let _ = (*ptr).is_empty(); + | ^^^^^^ +note: method calls to `is_empty` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).is_empty(); | ++ + -warning: 18 warnings emitted +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^-----^^^^^^^^^^^ + | | + | this raw pointer has type `*const [T]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[T]` + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*slice)[..]).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^-----^^^^^ + | | + | this raw pointer has type `*const [T]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[T]` + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^^^^^^^ +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*slice))[..].len(); + | ++ + + +warning: 20 warnings emitted diff --git a/tests/ui/parser/ternary_operator.rs b/tests/ui/parser/ternary_operator.rs index c8810781b3d8..08f6a4b2a244 100644 --- a/tests/ui/parser/ternary_operator.rs +++ b/tests/ui/parser/ternary_operator.rs @@ -28,3 +28,9 @@ fn main() { //~| HELP use an `if-else` expression instead //~| ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `:` } + +fn expr(a: u64, b: u64) -> u64 { + a > b ? a : b + //~^ ERROR Rust has no ternary operator + //~| HELP use an `if-else` expression instead +} diff --git a/tests/ui/parser/ternary_operator.stderr b/tests/ui/parser/ternary_operator.stderr index e12a7ff3718e..d4a633e5e557 100644 --- a/tests/ui/parser/ternary_operator.stderr +++ b/tests/ui/parser/ternary_operator.stderr @@ -2,7 +2,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:2:19 | LL | let x = 5 > 2 ? true : false; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -10,7 +10,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:8:19 | LL | let x = 5 > 2 ? { true } : { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -18,7 +18,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:14:19 | LL | let x = 5 > 2 ? f32::MAX : f32::MIN; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -38,9 +38,21 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:26:19 | LL | let x = 5 > 2 ? { let x = vec![]: Vec; x } : { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead -error: aborting due to 6 previous errors +error: Rust has no ternary operator + --> $DIR/ternary_operator.rs:33:5 + | +LL | a > b ? a : b + | ^^^^^^^^^^^^^ + | +help: use an `if-else` expression instead + | +LL - a > b ? a : b +LL + if a > b { a } else { b } + | + +error: aborting due to 7 previous errors diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs new file mode 100644 index 000000000000..83ad8c76bb1c --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs @@ -0,0 +1,81 @@ +//! Test that guard patterns can see bindings already in scope and bindings introduced in their +//! subpattern, but no other bindings from the containing pattern. Also make sure bindings +//! introduced in guard patterns are visible in fn/arm/loop/etc bodies. + +#![feature(guard_patterns)] +#![expect(incomplete_features)] + +fn good_fn_item(((x if x) | x): bool) -> bool { x } + +fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} +//~^ ERROR cannot find value `x` in this scope +fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} +//~^ ERROR cannot find value `y` in this scope + +fn main() { + let ((local if local) if local) = false; + + match (true, true) { + (x if local, y if good_fn_item(y)) => x && y, + (x, y if x) => x && y, + //~^ ERROR cannot find value `x` in this scope + (x if y, y) => x && y, + //~^ ERROR cannot find value `y` in this scope + }; + + match (true,) { + (x @ y if x && y,) => x && y, + (x @ (y if y),) => x && y, + (x @ (y if x),) => x && y, + //~^ ERROR cannot find value `x` in this scope + }; + + match (Ok(true),) { + ((Ok(x) | Err(x)) if good_fn_item(x),) => x, + ((Ok(x) if local) | (Err(x) if good_fn_item(x)),) => x, + ((Ok(x if x) if x) | (Err(x if x) if x) if x,) if x => x, + ((Ok(x) if y) | (Err(y) if x),) => x && y, + //~^ ERROR variable `x` is not bound in all patterns + //~| ERROR variable `y` is not bound in all patterns + //~| ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + }; + + let (_ if nonexistent) = true; + //~^ ERROR cannot find value `nonexistent` in this scope + if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + (|(x if x), (y if y)| x && y)(true, true); + (|(x if y), (y if x)| x && y)(true, true); + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + // FIXME(guard_patterns): mismatched bindings are not yet allowed + match Some(0) { + Some(x if x > 0) | None => {} + //~^ ERROR variable `x` is not bound in all patterns + } +} + +/// Make sure shadowing is handled properly. In particular, if a pattern shadows an identifier, +/// a guard pattern's guard should still see the original binding if the shadowing binding isn't in +/// its subpattern. +fn test_shadowing(local: bool) -> u8 { + match (0, 0) { + // The `local` binding here shadows the `bool` definition, so we get a type error. + //~v ERROR mismatched types + local if local => 0, + // The guards here should see the `bool` definition of `local`, not the new `u8` binding. + // The body should see the new binding. + (local, _ if local) => local, + (_ if local, local) => local, + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr new file mode 100644 index 000000000000..d76e60478a14 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -0,0 +1,133 @@ +error[E0408]: variable `y` is not bound in all patterns + --> $DIR/name-resolution.rs:37:10 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^^^^^^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `y` + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:37:25 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | - ^^^^^^^^^^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:63:28 + | +LL | Some(x if x > 0) | None => {} + | - ^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:10:34 + | +LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:12:25 + | +LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:20:18 + | +LL | (x, y if x) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:22:15 + | +LL | (x if y, y) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:29:20 + | +LL | (x @ (y if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:37:20 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:37:36 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `nonexistent` in this scope + --> $DIR/name-resolution.rs:44:15 + | +LL | let (_ if nonexistent) = true; + | ^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:46:22 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:46:33 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:49:25 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:49:36 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:52:19 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:52:30 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:57:13 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:57:23 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `y` + +error[E0308]: mismatched types + --> $DIR/name-resolution.rs:75:18 + | +LL | local if local => 0, + | ^^^^^ expected `bool`, found `({integer}, {integer})` + | + = note: expected type `bool` + found tuple `({integer}, {integer})` + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0308, E0408, E0425. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs index 2786251c7951..77f186152487 100644 --- a/tests/ui/simd/target-feature-mixup.rs +++ b/tests/ui/simd/target-feature-mixup.rs @@ -7,7 +7,6 @@ //@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590) #![feature(repr_simd, target_feature, cfg_target_feature)] -#![feature(avx512_target_feature)] use std::process::{Command, ExitStatus}; use std::env; diff --git a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs index 2682028936c1..15bcfdd9076e 100644 --- a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs +++ b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs @@ -1,5 +1,5 @@ -#![feature(avx512_target_feature)] +#![feature(x87_target_feature)] #[inline] -#[target_feature(enable = "avx512ifma")] +#[target_feature(enable = "x87")] pub unsafe fn foo() {} diff --git a/tests/ui/target-feature/gate.rs b/tests/ui/target-feature/gate.rs index 14fdad02f566..9244a98d82fd 100644 --- a/tests/ui/target-feature/gate.rs +++ b/tests/ui/target-feature/gate.rs @@ -2,7 +2,6 @@ // // gate-test-sse4a_target_feature // gate-test-powerpc_target_feature -// gate-test-avx512_target_feature // gate-test-tbm_target_feature // gate-test-arm_target_feature // gate-test-hexagon_target_feature @@ -27,7 +26,7 @@ // gate-test-x87_target_feature // gate-test-m68k_target_feature -#[target_feature(enable = "avx512bw")] +#[target_feature(enable = "x87")] //~^ ERROR: currently unstable unsafe fn foo() {} diff --git a/tests/ui/target-feature/gate.stderr b/tests/ui/target-feature/gate.stderr index fa876893848f..32d60ce43822 100644 --- a/tests/ui/target-feature/gate.stderr +++ b/tests/ui/target-feature/gate.stderr @@ -1,11 +1,11 @@ -error[E0658]: the target feature `avx512bw` is currently unstable - --> $DIR/gate.rs:30:18 +error[E0658]: the target feature `x87` is currently unstable + --> $DIR/gate.rs:29:18 | -LL | #[target_feature(enable = "avx512bw")] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "x87")] + | ^^^^^^^^^^^^^^ | = note: see issue #44839 for more information - = help: add `#![feature(avx512_target_feature)]` to the crate attributes to enable + = help: add `#![feature(x87_target_feature)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/unstable-feature.rs b/tests/ui/target-feature/unstable-feature.rs index f62c4dd938a0..a79ad4696033 100644 --- a/tests/ui/target-feature/unstable-feature.rs +++ b/tests/ui/target-feature/unstable-feature.rs @@ -1,8 +1,8 @@ -//@ compile-flags: -Ctarget-feature=+vaes --crate-type=rlib --target=x86_64-unknown-linux-gnu +//@ compile-flags: -Ctarget-feature=+x87 --crate-type=rlib --target=x86_64-unknown-linux-gnu //@ build-pass //@ needs-llvm-components: x86 #![feature(no_core)] #![no_core] -//~? WARN unstable feature specified for `-Ctarget-feature`: `vaes` +//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/unstable-feature.stderr b/tests/ui/target-feature/unstable-feature.stderr index d34544c5c277..309b64afd922 100644 --- a/tests/ui/target-feature/unstable-feature.stderr +++ b/tests/ui/target-feature/unstable-feature.stderr @@ -1,4 +1,4 @@ -warning: unstable feature specified for `-Ctarget-feature`: `vaes` +warning: unstable feature specified for `-Ctarget-feature`: `x87` | = note: this feature is not stably supported; its behavior can change in the future diff --git a/tests/ui/traits/object/ambiguity-vtable-segfault.rs b/tests/ui/traits/object/ambiguity-vtable-segfault.rs new file mode 100644 index 000000000000..dff7d26ae933 --- /dev/null +++ b/tests/ui/traits/object/ambiguity-vtable-segfault.rs @@ -0,0 +1,37 @@ +// In this example below, we have two overlapping candidates for `dyn Q: Q`. +// Specifically, the user written impl for `::Assoc` and the +// built-in impl for object types. Since they differ by their region responses, +// the goal is ambiguous. This affects codegen since impossible obligations +// for method dispatch will lead to a segfault, since we end up emitting dummy +// call vtable offsets due to . + +// Test for . + +//@ run-pass + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +trait Q: 'static { + fn q(&self); +} + +impl Q for i32 { + fn q(&self) { println!("i32"); } +} + +impl Q for ::Assoc where Self: 'static { + fn q(&self) { println!("dyn Q"); } +} + +fn foo(t: &T) { + t.q(); +} + +fn main() { + foo(&1 as &dyn Q); +} diff --git a/tests/ui/traits/vtable/impossible-method.rs b/tests/ui/traits/vtable/impossible-method.rs new file mode 100644 index 000000000000..ff7910cfac86 --- /dev/null +++ b/tests/ui/traits/vtable/impossible-method.rs @@ -0,0 +1,38 @@ +//@ run-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Id { + type This<'a>; +} +impl Id for T { + type This<'a> = T; +} + +trait Trait {} +impl Trait fn(T::This<'a>)> for T {} + +trait Method { + fn call_me(&self) + where + T: Trait fn(T::This<'a>)>; +} + +impl Method for T { + fn call_me(&self) { + println!("method was reachable"); + } +} + +fn generic(x: &dyn Method) { + // Proving `T: Trait fn(T::This<'a>)>` holds. + x.call_me(); +} + +fn main() { + // Proving `u32: Trait` fails due to incompleteness. + // We don't add the method to the vtable of `dyn Method`, so + // calling it causes UB. + generic::(&()); +} diff --git a/tests/ui/type-alias-impl-trait/auxiliary/coherence_cross_crate_trait_decl.rs b/tests/ui/type-alias-impl-trait/coherence/auxiliary/coherence_cross_crate_trait_decl.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/auxiliary/coherence_cross_crate_trait_decl.rs rename to tests/ui/type-alias-impl-trait/coherence/auxiliary/coherence_cross_crate_trait_decl.rs diff --git a/tests/ui/type-alias-impl-trait/auxiliary/foreign-crate.rs b/tests/ui/type-alias-impl-trait/coherence/auxiliary/foreign-crate.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/auxiliary/foreign-crate.rs rename to tests/ui/type-alias-impl-trait/coherence/auxiliary/foreign-crate.rs diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.rs b/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.rs similarity index 100% rename from tests/ui/impl-trait/coherence-treats-tait-ambig.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.rs diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.stderr similarity index 100% rename from tests/ui/impl-trait/coherence-treats-tait-ambig.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence.classic.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence.classic.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence.classic.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence.classic.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence.next.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence.next.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence.next.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence.next.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence.rs b/tests/ui/type-alias-impl-trait/coherence/coherence.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence.rs diff --git a/tests/ui/type-alias-impl-trait/coherence_cross_crate.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_cross_crate.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.rs diff --git a/tests/ui/type-alias-impl-trait/coherence_cross_crate.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_cross_crate.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs diff --git a/tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence_generalization.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_generalization.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_generalization.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_generalization.rs diff --git a/tests/ui/typeck/gather-locals-twice.rs b/tests/ui/typeck/gather-locals-twice.rs new file mode 100644 index 000000000000..e9351146f456 --- /dev/null +++ b/tests/ui/typeck/gather-locals-twice.rs @@ -0,0 +1,7 @@ +// Regression test for . + +fn main() { + () += { let x; }; + //~^ ERROR binary assignment operation `+=` cannot be applied to type `()` + //~| ERROR invalid left-hand side of assignment +} diff --git a/tests/ui/typeck/gather-locals-twice.stderr b/tests/ui/typeck/gather-locals-twice.stderr new file mode 100644 index 000000000000..7598ef8e7ea4 --- /dev/null +++ b/tests/ui/typeck/gather-locals-twice.stderr @@ -0,0 +1,20 @@ +error[E0368]: binary assignment operation `+=` cannot be applied to type `()` + --> $DIR/gather-locals-twice.rs:4:5 + | +LL | () += { let x; }; + | --^^^^^^^^^^^^^^ + | | + | cannot use `+=` on type `()` + +error[E0067]: invalid left-hand side of assignment + --> $DIR/gather-locals-twice.rs:4:8 + | +LL | () += { let x; }; + | -- ^^ + | | + | cannot assign to this expression + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0067, E0368. +For more information about an error, try `rustc --explain E0067`. diff --git a/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs new file mode 100644 index 000000000000..d71a7ff1d3d4 --- /dev/null +++ b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs @@ -0,0 +1,56 @@ +trait Reencode { + type Error; + fn tag_index(&mut self, tag: u32) -> Result; +} + +struct Reencoder; +impl Reencode for Reencoder { + type Error = &'static str; + fn tag_index(&mut self, tag: u32) -> Result { + Ok(tag) + } +} + + +enum Operator { + Suspend { tag_index: u32 }, +} + +enum Instruction { + Suspend { tag_index: u32 }, +} + + +macro_rules! for_each_operator { + ($m:ident) => { + $m! { + Suspend { tag_index: u32 } => visit_suspend + } + }; +} + + +fn process(op: &Operator, reencoder: &mut T) -> Instruction { + macro_rules! translate { + (Suspend { tag_index: $ty:ty } => $visit:ident) => { + match op { + Operator::Suspend { tag_index } => { + let tag_index = reencoder.tag_index(*tag_index); + + // KEY POINT: Using field shorthand syntax where the compiler gets confused + // Here tag_index is a Result but we're using it where u32 is expected + Instruction::Suspend { tag_index } //~ ERROR mismatched types [E0308] + } + } + }; + } + + for_each_operator!(translate) +} + +fn main() { + let mut reencoder = Reencoder; + let op = Operator::Suspend { tag_index: 1 }; + + let _ = process(&op, &mut reencoder); +} diff --git a/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr new file mode 100644 index 000000000000..12537754d802 --- /dev/null +++ b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/macro-shorthand-issue-140659.rs:42:44 + | +LL | Instruction::Suspend { tag_index } + | ^^^^^^^^^ expected `u32`, found `Result::Error>` +... +LL | for_each_operator!(translate) + | ----------------------------- in this macro invocation + | + = note: expected type `u32` + found enum `Result::Error>` + = note: this error originates in the macro `translate` which comes from the expansion of the macro `for_each_operator` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.fixed similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.fixed diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.rs similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.rs diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.stderr similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.stderr diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.rs b/tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.rs similarity index 100% rename from tests/ui/typeck/suggest-arg-comma-delete-ice.rs rename to tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.rs diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr b/tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.stderr similarity index 100% rename from tests/ui/typeck/suggest-arg-comma-delete-ice.stderr rename to tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.stderr diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.fixed similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.fixed diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.rs similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.rs diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.stderr similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.stderr diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs b/tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.rs similarity index 100% rename from tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs rename to tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.rs diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr b/tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.stderr similarity index 100% rename from tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr rename to tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.stderr diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.rs b/tests/ui/where-clauses/method-suggestion-no-duplication.rs similarity index 100% rename from tests/ui/impl-trait/method-suggestion-no-duplication.rs rename to tests/ui/where-clauses/method-suggestion-no-duplication.rs diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.stderr b/tests/ui/where-clauses/method-suggestion-no-duplication.stderr similarity index 100% rename from tests/ui/impl-trait/method-suggestion-no-duplication.stderr rename to tests/ui/where-clauses/method-suggestion-no-duplication.stderr diff --git a/triagebot.toml b/triagebot.toml index 9dcdbcecbeca..3afa2d364109 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -133,7 +133,7 @@ In case it's useful, here are some [instructions] for tackling these sorts of is [instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/rust-for-linux.html """ -label = "O-rfl" +label = "A-rust-for-linux" [ping.wasm] alias = ["webassembly"] @@ -1424,3 +1424,6 @@ compiletest = [ # Enable `@rustbot note` functionality # Documentation at: https://forge.rust-lang.org/triagebot/note.html [note] + +[behind-upstream] +days-threshold = 14