Merge from rustc

This commit is contained in:
The Miri Conjob Bot 2024-01-13 05:03:10 +00:00
commit 9b46f3bb64
400 changed files with 7538 additions and 2398 deletions

View file

@ -4,18 +4,18 @@ version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anstyle"
version = "1.0.0"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "autocfg"
@ -37,9 +37,9 @@ checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "block-buffer"
version = "0.10.2"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
@ -79,13 +79,13 @@ dependencies = [
[[package]]
name = "bstr"
version = "0.2.17"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
@ -110,9 +110,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.4.7"
version = "4.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
dependencies = [
"clap_builder",
"clap_derive",
@ -120,9 +120,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.4.7"
version = "4.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
dependencies = [
"anstyle",
"clap_lex",
@ -130,9 +130,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.4.3"
version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7"
checksum = "97aeaa95557bd02f23fbb662f981670c3d20c5a26e69f7354b28f57092437fcd"
dependencies = [
"clap",
]
@ -166,24 +166,24 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
version = "0.2.5"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@ -192,31 +192,29 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.13"
version = "0.9.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.3"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
@ -224,15 +222,15 @@ dependencies = [
[[package]]
name = "diff"
version = "0.1.12"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.10.3"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
@ -240,9 +238,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.6.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "errno"
@ -267,27 +265,21 @@ dependencies = [
[[package]]
name = "filetime"
version = "0.2.16"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"winapi",
"windows-sys",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "generic-array"
version = "0.14.5"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
@ -295,15 +287,15 @@ dependencies = [
[[package]]
name = "globset"
version = "0.4.8"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd"
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
"regex-automata",
"regex-syntax",
]
[[package]]
@ -314,36 +306,34 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "home"
version = "0.5.4"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"winapi",
"windows-sys",
]
[[package]]
name = "ignore"
version = "0.4.18"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060"
dependencies = [
"crossbeam-utils",
"crossbeam-deque",
"globset",
"lazy_static",
"log",
"memchr",
"regex",
"regex-automata",
"same-file",
"thread_local",
"walkdir",
"winapi-util",
]
[[package]]
name = "itoa"
version = "1.0.2"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "junction"
@ -355,17 +345,11 @@ dependencies = [
"winapi",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "linux-raw-sys"
@ -375,18 +359,15 @@ checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "log"
version = "0.4.17"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "lzma-sys"
version = "0.1.17"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619"
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
dependencies = [
"cc",
"libc",
@ -395,33 +376,24 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.5.0"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "ntapi"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
"winapi",
]
[[package]]
name = "object"
version = "0.32.0"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
@ -434,9 +406,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "opener"
version = "0.5.0"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005"
dependencies = [
"bstr",
"winapi",
@ -444,9 +416,9 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.25"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "pretty_assertions"
@ -460,18 +432,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.60"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@ -498,35 +470,29 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.2.13"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.5.6"
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.26"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustix"
@ -543,9 +509,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.10"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "same-file"
@ -558,27 +524,30 @@ dependencies = [
[[package]]
name = "scopeguard"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.17"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
version = "1.0.160"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.160"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
@ -587,9 +556,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.81"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa",
"ryu",
@ -598,9 +567,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@ -609,9 +578,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.8"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
@ -620,9 +589,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.30.3"
version = "0.30.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba2dbd2894d23b2d78dae768d85e323b557ac3ac71a5d917a31536d8f77ebada"
checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2"
dependencies = [
"cfg-if",
"core-foundation-sys",
@ -635,9 +604,9 @@ dependencies = [
[[package]]
name = "tar"
version = "0.4.38"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb"
dependencies = [
"filetime",
"libc",
@ -646,42 +615,33 @@ dependencies = [
[[package]]
name = "termcolor"
version = "1.2.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "toml"
version = "0.5.9"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "typenum"
version = "1.15.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
@ -691,12 +651,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.3.2"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
@ -718,9 +677,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
@ -818,18 +777,20 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "xattr"
version = "0.2.3"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1"
dependencies = [
"libc",
"linux-raw-sys",
"rustix",
]
[[package]]
name = "xz2"
version = "0.1.6"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c"
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
dependencies = [
"lzma-sys",
]

View file

@ -33,30 +33,34 @@ path = "src/bin/sccache-plus-cl.rs"
test = false
[dependencies]
# Most of the time updating these dependencies requires modifications
# to the bootstrap codebase; otherwise, some targets will fail. That's
# why these dependencies are explicitly pinned.
cc = "=1.0.73"
cmake = "=0.1.48"
build_helper = { path = "../tools/build_helper" }
cc = "1.0.69"
clap = { version = "4.4.7", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] }
clap_complete = "4.4.3"
cmake = "0.1.38"
clap = { version = "4.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] }
clap_complete = "4.4"
fd-lock = "4.0"
filetime = "0.2"
home = "0.5.4"
ignore = "0.4.10"
libc = "0.2.150"
object = { version = "0.32.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] }
once_cell = "1.7.2"
home = "0.5"
ignore = "0.4"
libc = "0.2"
object = { version = "0.32", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] }
once_cell = "1.19"
opener = "0.5"
semver = "1.0.17"
serde = "1.0.137"
semver = "1.0"
serde = "1.0"
# Directly use serde_derive rather than through the derive feature of serde to allow building both
# in parallel and to allow serde_json and toml to start building as soon as serde has been built.
serde_derive = "1.0.137"
serde_json = "1.0.2"
serde_derive = "1.0"
serde_json = "1.0"
sha2 = "0.10"
tar = "0.4"
termcolor = "1.2.0"
termcolor = "1.4"
toml = "0.5"
walkdir = "2"
walkdir = "2.4"
xz2 = "0.1"
# Dependencies needed by the build-metrics feature

View file

@ -1776,7 +1776,6 @@ impl Config {
check_ci_llvm!(static_libstdcpp);
check_ci_llvm!(targets);
check_ci_llvm!(experimental_targets);
check_ci_llvm!(link_jobs);
check_ci_llvm!(clang_cl);
check_ci_llvm!(version_suffix);
check_ci_llvm!(cflags);

View file

@ -32,9 +32,12 @@ fn download_ci_llvm() {
assert_eq!(parse_llvm("rust.channel = \"dev\""), if_unchanged);
assert!(!parse_llvm("rust.channel = \"stable\""));
assert_eq!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\""), if_unchanged);
assert_eq!(parse_llvm(
"llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-unchanged\""
), if_unchanged);
assert_eq!(
parse_llvm(
"llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-unchanged\""
),
if_unchanged
);
assert!(!parse_llvm(
"llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-unchanged\""
));

View file

@ -1,4 +1,4 @@
use crate::utils::helpers::{extract_beta_rev, hex_encode, make, check_cfg_arg};
use crate::utils::helpers::{check_cfg_arg, extract_beta_rev, hex_encode, make};
use std::path::PathBuf;
#[test]

View file

@ -1,6 +1,8 @@
#!/bin/bash
# This script dumps information about the build environment to stdout.
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
set -euo pipefail
IFS=$'\n\t'
@ -17,3 +19,17 @@ set +o pipefail
du . | sort -nr | head -n100
set -o pipefail
echo
if isMacOS
then
# Debugging information that might be helpful for diagnosing macOS
# performance issues.
# SIP
csrutil status
# Gatekeeper
spctl --status
# Authorization policy
DevToolsSecurity -status
# Spotlight status
mdutil -avs
fi

View file

@ -1,4 +1,4 @@
# `env`
# `env-set`
The tracking issue for this feature is: [#118372](https://github.com/rust-lang/rust/issues/118372).
@ -11,11 +11,11 @@ from the `proc_macro` crate.
This information will be stored in the dep-info files. For more information about
dep-info files, take a look [here](https://doc.rust-lang.org/cargo/guide/build-cache.html#dep-info-files).
When retrieving an environment variable value, the one specified by `--env` will take
When retrieving an environment variable value, the one specified by `--env-set` will take
precedence. For example, if you want have `PATH=a` in your environment and pass:
```bash
rustc --env PATH=env
rustc --env-set PATH=env
```
Then you will have:
@ -24,17 +24,17 @@ Then you will have:
assert_eq!(env!("PATH"), "env");
```
It will trigger a new compilation if any of the `--env` argument value is different.
It will trigger a new compilation if any of the `--env-set` argument value is different.
So if you first passed:
```bash
--env A=B --env X=12
--env-set A=B --env X=12
```
and then on next compilation:
```bash
--env A=B
--env-set A=B
```
`X` value is different (not set) so the code will be re-compiled.
@ -42,4 +42,4 @@ and then on next compilation:
Please note that on Windows, environment variables are case insensitive but case
preserving whereas `rustc`'s environment variables are case sensitive. For example,
having `Path` in your environment (case insensitive) is different than using
`rustc --env Path=...` (case sensitive).
`rustc --env-set Path=...` (case sensitive).

View file

@ -1761,4 +1761,8 @@ _x.py() {
esac
}
complete -F _x.py -o nosort -o bashdefault -o default x.py
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -F _x.py -o nosort -o bashdefault -o default x.py
else
complete -F _x.py -o bashdefault -o default x.py
fi

View file

@ -69,8 +69,8 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String
let mut parser =
match rustc_parse::maybe_new_parser_from_source_str(&sess, file_name, snippet.clone()) {
Ok(parser) => parser,
Err(diagnostics) => {
drop(diagnostics);
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
return None;
}
};

View file

@ -589,7 +589,7 @@ pub(crate) fn make_test(
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) {
Ok(p) => p,
Err(errs) => {
drop(errs);
errs.into_iter().for_each(|err| err.cancel());
return (found_main, found_extern_crate, found_macro);
}
};
@ -759,8 +759,10 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
let mut parser =
match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) {
Ok(p) => p,
Err(_) => {
// If there is an unclosed delimiter, an error will be returned by the tokentrees.
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
// If there is an unclosed delimiter, an error will be returned by the
// tokentrees.
return false;
}
};

@ -1 +1 @@
Subproject commit 606bc11367b475542bd6228163424ca43b4dbdbc
Subproject commit 700fbf978e6c5bb297d12963206f7487722de480

@ -1 +1 @@
Subproject commit 3e428a38a34e820a461d2cc082e726d3bda71bcb
Subproject commit 84976cd699f4aea56cb3a90ce3eedeed9e20d5a5

View file

@ -38,7 +38,7 @@ jobs:
github_token: "${{ secrets.github_token }}"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install toolchain
run: rustup show active-toolchain

View file

@ -26,7 +26,7 @@ jobs:
github_token: "${{ secrets.github_token }}"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
@ -72,7 +72,7 @@ jobs:
github_token: "${{ secrets.github_token }}"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install i686 dependencies
if: matrix.host == 'i686-unknown-linux-gnu'
@ -151,7 +151,7 @@ jobs:
github_token: "${{ secrets.github_token }}"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install toolchain
run: rustup show active-toolchain
@ -175,7 +175,7 @@ jobs:
github_token: "${{ secrets.github_token }}"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install toolchain
run: rustup show active-toolchain
@ -231,7 +231,7 @@ jobs:
github_token: "${{ secrets.github_token }}"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install toolchain
run: rustup show active-toolchain

View file

@ -24,7 +24,7 @@ jobs:
steps:
# Setup
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
# Run
- name: Build

View file

@ -21,10 +21,10 @@ jobs:
steps:
# Setup
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ env.TARGET_BRANCH }}
path: 'out'

View file

@ -16,7 +16,7 @@ jobs:
steps:
# Setup
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v3

View file

@ -5105,6 +5105,7 @@ Released 2018-09-13
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
[`empty_enum_variants_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum_variants_with_brackets
[`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
@ -5294,6 +5295,7 @@ Released 2018-09-13
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
[`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
@ -5440,6 +5442,7 @@ Released 2018-09-13
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
[`option_as_ref_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_cloned
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
@ -5483,6 +5486,7 @@ Released 2018-09-13
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
[`pub_underscore_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
[`pub_with_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_with_shorthand
[`pub_without_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_without_shorthand
@ -5580,6 +5584,7 @@ Released 2018-09-13
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
[`str_split_at_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_split_at_newline
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
@ -5612,6 +5617,7 @@ Released 2018-09-13
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
[`test_attr_in_doctest`]: https://rust-lang.github.io/rust-clippy/master/index.html#test_attr_in_doctest
[`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module
[`thread_local_initializer_can_be_made_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#thread_local_initializer_can_be_made_const
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
@ -5810,4 +5816,5 @@ Released 2018-09-13
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
<!-- end autogenerated links to configuration documentation -->

View file

@ -1,6 +1,6 @@
// REUSE-IgnoreStart
Copyright 2014-2022 The Rust Project Developers
Copyright 2014-2024 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license

View file

@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014-2022 The Rust Project Developers
Copyright 2014-2024 The Rust Project Developers
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2014-2022 The Rust Project Developers
Copyright (c) 2014-2024 The Rust Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated

View file

@ -5,7 +5,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
@ -278,7 +278,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
<!-- REUSE-IgnoreStart -->
Copyright 2014-2023 The Rust Project Developers
Copyright 2014-2024 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license

View file

@ -6,7 +6,7 @@
A collection of lints to catch common mistakes and improve your
[Rust](https://github.com/rust-lang/rust) code.
[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
Lints are divided into categories, each with a default [lint
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how

View file

@ -9,6 +9,7 @@
- [Clippy's Lints](lints.md)
- [Continuous Integration](continuous_integration/README.md)
- [GitHub Actions](continuous_integration/github_actions.md)
- [GitLab CI](continuous_integration/gitlab.md)
- [Travis CI](continuous_integration/travis.md)
- [Development](development/README.md)
- [Basics](development/basics.md)

View file

@ -15,7 +15,7 @@ jobs:
clippy_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Run Clippy
run: cargo clippy --all-targets --all-features
```

View file

@ -0,0 +1,16 @@
# GitLab CI
You can add Clippy to GitLab CI by using the latest stable [rust docker image](https://hub.docker.com/_/rust),
as it is shown in the `.gitlab-ci.yml` CI configuration file below,
```yml
# Make sure CI fails on all warnings, including Clippy lints
variables:
RUSTFLAGS: "-Dwarnings"
clippy_check:
image: rust:latest
script:
- rustup component add clippy
- cargo clippy --all-targets --all-features
```

View file

@ -102,7 +102,7 @@ let x: Option<u32> = Some(42);
m!(x, x.unwrap());
```
If the `m!(x, x.unwrapp());` line is expanded, we would get two expanded
If the `m!(x, x.unwrap());` line is expanded, we would get two expanded
expressions:
- `x.is_some()` (from the `$a.is_some()` line in the `m` macro)

View file

@ -133,7 +133,7 @@ in this chapter:
- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Adt
[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Adt
[AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html
[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
[node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type
@ -144,7 +144,7 @@ in this chapter:
[LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/typeck_results/struct.TypeckResults.html#method.pat_ty
[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html
[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
[middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html
[hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html

View file

@ -805,3 +805,13 @@ for _ in &mut *rmvec {}
* [`missing_errors_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc)
## `pub-underscore-fields-behavior`
**Default Value:** `"PublicallyExported"`
---
**Affected lints:**
* [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields)

View file

@ -1,5 +1,5 @@
use crate::msrvs::Msrv;
use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, Rename};
use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
use crate::ClippyConfiguration;
use rustc_data_structures::fx::FxHashSet;
use rustc_session::Session;
@ -547,6 +547,11 @@ define_Conf! {
///
/// Whether to also run the listed lints on private items.
(check_private_items: bool = false),
/// Lint: PUB_UNDERSCORE_FIELDS
///
/// Lint "public" fields in a struct that are prefixed with an underscore based on their
/// exported visibility, or whether they are marked as "pub".
(pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PublicallyExported),
}
/// Search for the configuration file.

View file

@ -96,6 +96,9 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
doc_comment.make_ascii_lowercase();
let lints: Vec<String> = doc_comment
.split_off(DOC_START.len())
.lines()
.next()
.unwrap()
.split(", ")
.map(str::to_string)
.collect();

View file

@ -17,7 +17,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN }
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,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }

View file

@ -126,3 +126,9 @@ unimplemented_serialize! {
Rename,
MacroMatcher,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum PubUnderscoreFieldsBehaviour {
PublicallyExported,
AllPubFields,
}

View file

@ -67,7 +67,7 @@ pub fn create(
if pass == "early" {
println!(
"\n\
NOTE: Use a late pass unless you need something specific from\
NOTE: Use a late pass unless you need something specific from\n\
an early pass, as they lack many features and utilities"
);
}

View file

@ -1,12 +1,14 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{method_chain_args, sext};
use rustc_hir::{Expr, ExprKind};
use clippy_utils::{clip, method_chain_args, sext};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, Ty, UintTy};
use super::CAST_SIGN_LOSS;
const METHODS_RET_POSITIVE: &[&str] = &["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
if should_lint(cx, cast_op, cast_from, cast_to) {
span_lint(
@ -25,33 +27,28 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
return false;
}
// Don't lint for positive constants.
let const_val = constant(cx, cx.typeck_results(), cast_op);
if let Some(Constant::Int(n)) = const_val
&& let ty::Int(ity) = *cast_from.kind()
&& sext(cx.tcx, n, ity) >= 0
{
// Don't lint if `cast_op` is known to be positive.
if let Sign::ZeroOrPositive = expr_sign(cx, cast_op, cast_from) {
return false;
}
// Don't lint for the result of methods that always return non-negative values.
if let ExprKind::MethodCall(path, ..) = cast_op.kind {
let mut method_name = path.ident.name.as_str();
let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
if method_name == "unwrap"
&& let Some(arglist) = method_chain_args(cast_op, &["unwrap"])
&& let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind
{
method_name = inner_path.ident.name.as_str();
}
if allowed_methods.iter().any(|&name| method_name == name) {
return false;
}
let (mut uncertain_count, mut negative_count) = (0, 0);
// Peel off possible binary expressions, e.g. x * x * y => [x, x, y]
let Some(exprs) = exprs_with_selected_binop_peeled(cast_op) else {
// Assume cast sign lose if we cannot determine the sign of `cast_op`
return true;
};
for expr in exprs {
let ty = cx.typeck_results().expr_ty(expr);
match expr_sign(cx, expr, ty) {
Sign::Negative => negative_count += 1,
Sign::Uncertain => uncertain_count += 1,
Sign::ZeroOrPositive => (),
};
}
true
// Lint if there are odd number of uncertain or negative results
uncertain_count % 2 == 1 || negative_count % 2 == 1
},
(false, true) => !cast_to.is_signed(),
@ -59,3 +56,97 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
(_, _) => false,
}
}
fn get_const_int_eval(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Option<i128> {
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
&& let ty::Int(ity) = *ty.kind()
{
return Some(sext(cx.tcx, n, ity));
}
None
}
enum Sign {
ZeroOrPositive,
Negative,
Uncertain,
}
fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
// Try evaluate this expr first to see if it's positive
if let Some(val) = get_const_int_eval(cx, expr, ty) {
return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative };
}
// Calling on methods that always return non-negative values.
if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind {
let mut method_name = path.ident.name.as_str();
if method_name == "unwrap"
&& let Some(arglist) = method_chain_args(expr, &["unwrap"])
&& let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind
{
method_name = inner_path.ident.name.as_str();
}
if method_name == "pow"
&& let [arg] = args
{
return pow_call_result_sign(cx, caller, arg);
} else if METHODS_RET_POSITIVE.iter().any(|&name| method_name == name) {
return Sign::ZeroOrPositive;
}
}
Sign::Uncertain
}
/// Return the sign of the `pow` call's result.
///
/// If the caller is a positive number, the result is always positive,
/// If the `power_of` is a even number, the result is always positive as well,
/// Otherwise a [`Sign::Uncertain`] will be returned.
fn pow_call_result_sign(cx: &LateContext<'_>, caller: &Expr<'_>, power_of: &Expr<'_>) -> Sign {
let caller_ty = cx.typeck_results().expr_ty(caller);
if let Some(caller_val) = get_const_int_eval(cx, caller, caller_ty)
&& caller_val >= 0
{
return Sign::ZeroOrPositive;
}
if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), power_of)
&& clip(cx.tcx, n, UintTy::U32) % 2 == 0
{
return Sign::ZeroOrPositive;
}
Sign::Uncertain
}
/// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
/// which the result could always be positive under certain condition.
///
/// Other operators such as `+`/`-` causing the result's sign hard to determine, which we will
/// return `None`
fn exprs_with_selected_binop_peeled<'a>(expr: &'a Expr<'_>) -> Option<Vec<&'a Expr<'a>>> {
#[inline]
fn collect_operands<'a>(expr: &'a Expr<'a>, operands: &mut Vec<&'a Expr<'a>>) -> Option<()> {
match expr.kind {
ExprKind::Binary(op, lhs, rhs) => {
if matches!(op.node, BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem) {
collect_operands(lhs, operands);
operands.push(rhs);
} else {
// Things are complicated when there are other binary ops exist,
// abort checking by returning `None` for now.
return None;
}
},
_ => operands.push(expr),
}
Some(())
}
let mut res = vec![];
collect_operands(expr, &mut res)?;
Some(res)
}

View file

@ -150,7 +150,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
crate::empty_drop::EMPTY_DROP_INFO,
crate::empty_enum::EMPTY_ENUM_INFO,
crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO,
crate::empty_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,
crate::endian_bytes::HOST_ENDIAN_BYTES_INFO,
crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO,
@ -385,6 +386,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
crate::methods::MANUAL_FILTER_MAP_INFO,
crate::methods::MANUAL_FIND_MAP_INFO,
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
crate::methods::MANUAL_NEXT_BACK_INFO,
crate::methods::MANUAL_OK_OR_INFO,
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
@ -408,6 +410,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::NO_EFFECT_REPLACE_INFO,
crate::methods::OBFUSCATED_IF_ELSE_INFO,
crate::methods::OK_EXPECT_INFO,
crate::methods::OPTION_AS_REF_CLONED_INFO,
crate::methods::OPTION_AS_REF_DEREF_INFO,
crate::methods::OPTION_FILTER_MAP_INFO,
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
@ -433,6 +436,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
crate::methods::STRING_EXTEND_CHARS_INFO,
crate::methods::STRING_LIT_CHARS_ANY_INFO,
crate::methods::STR_SPLIT_AT_NEWLINE_INFO,
crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO,
crate::methods::SUSPICIOUS_MAP_INFO,
crate::methods::SUSPICIOUS_SPLITN_INFO,
@ -576,6 +580,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::ptr::MUT_FROM_REF_INFO,
crate::ptr::PTR_ARG_INFO,
crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO,
crate::pub_use::PUB_USE_INFO,
crate::question_mark::QUESTION_MARK_INFO,
crate::question_mark_used::QUESTION_MARK_USED_INFO,
@ -648,6 +653,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
crate::thread_local_initializer_can_be_made_const::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST_INFO,
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,

View file

@ -4,7 +4,7 @@ use clippy_utils::{get_parent_node, numeric_literal};
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
use rustc_hir::{Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind};
use rustc_hir::{Block, Body, Expr, ExprKind, FnRetTy, HirId, ItemKind, Lit, Node, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
@ -122,13 +122,42 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
match &expr.kind {
ExprKind::Block(
Block {
stmts, expr: Some(_), ..
},
_,
) => {
if let Some(parent) = self.cx.tcx.hir().find_parent(expr.hir_id)
&& let Some(fn_sig) = parent.fn_sig()
&& let FnRetTy::Return(_ty) = fn_sig.decl.output
{
// We cannot check the exact type since it's a `hir::Ty`` which does not implement `is_numeric`
self.ty_bounds.push(ExplicitTyBound(true));
for stmt in *stmts {
self.visit_stmt(stmt);
}
self.ty_bounds.pop();
// Ignore return expr since we know its type was inferred from return ty
return;
}
},
// Ignore return expr since we know its type was inferred from return ty
ExprKind::Ret(_) => return,
ExprKind::Call(func, args) => {
if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) {
// Push found arg type, then visit arg.
self.ty_bounds.push((*bound).into());
self.visit_expr(expr);
self.ty_bounds.pop();
// If is from macro, try to use last bound type (typically pushed when visiting stmt),
// otherwise push found arg type, then visit arg,
if expr.span.from_expansion() {
self.visit_expr(expr);
} else {
self.ty_bounds.push((*bound).into());
self.visit_expr(expr);
self.ty_bounds.pop();
}
}
return;
}
@ -137,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
ExprKind::MethodCall(_, receiver, args, _) => {
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
let fn_sig = self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
for (expr, bound) in iter::zip(iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
self.ty_bounds.push((*bound).into());
self.visit_expr(expr);
self.ty_bounds.pop();

View file

@ -53,7 +53,7 @@ pub fn check(
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
Ok(p) => p,
Err(errs) => {
drop(errs);
errs.into_iter().for_each(|err| err.cancel());
return (false, test_attr_spans);
},
};

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{Item, ItemKind, VariantData};
use rustc_ast::ast::{Item, ItemKind, Variant, VariantData};
use rustc_errors::Applicability;
use rustc_lexer::TokenKind;
use rustc_lint::{EarlyContext, EarlyLintPass};
@ -27,9 +27,38 @@ declare_clippy_lint! {
restriction,
"finds struct declarations with empty brackets"
}
declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]);
impl EarlyLintPass for EmptyStructsWithBrackets {
declare_clippy_lint! {
/// ### What it does
/// Finds enum variants without fields that are declared with empty brackets.
///
/// ### Why is this bad?
/// Empty brackets while defining enum variants are redundant and can be omitted.
///
/// ### Example
/// ```no_run
/// enum MyEnum {
/// HasData(u8),
/// HasNoData(), // redundant parentheses
/// }
/// ```
///
/// Use instead:
/// ```no_run
/// enum MyEnum {
/// HasData(u8),
/// HasNoData,
/// }
/// ```
#[clippy::version = "1.77.0"]
pub EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
restriction,
"finds enum variants with empty brackets"
}
declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]);
impl EarlyLintPass for EmptyWithBrackets {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
let span_after_ident = item.span.with_lo(item.ident.span.hi());
@ -53,6 +82,27 @@ impl EarlyLintPass for EmptyStructsWithBrackets {
);
}
}
fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &Variant) {
let span_after_ident = variant.span.with_lo(variant.ident.span.hi());
if has_brackets(&variant.data) && has_no_fields(cx, &variant.data, span_after_ident) {
span_lint_and_then(
cx,
EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
span_after_ident,
"enum variant has empty brackets",
|diagnostic| {
diagnostic.span_suggestion_hidden(
span_after_ident,
"remove the brackets",
"",
Applicability::MaybeIncorrect,
);
},
);
}
}
}
fn has_no_ident_token(braces_span_str: &str) -> bool {

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
@ -74,6 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
return;
}
// `bool::then()` and `bool::then_some()` are not const
if in_constant(cx, expr.hir_id) {
return;
}
let ctxt = expr.span.ctxt();
if let Some(higher::If {

View file

@ -40,7 +40,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Lints subtraction between an [`Instant`] and a [`Duration`].
/// Lints subtraction between an `Instant` and a `Duration`.
///
/// ### Why is this bad?
/// Unchecked subtraction could cause underflow on certain platforms, leading to
@ -57,9 +57,6 @@ declare_clippy_lint! {
/// # use std::time::{Instant, Duration};
/// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
/// ```
///
/// [`Duration`]: std::time::Duration
/// [`Instant::now()`]: std::time::Instant::now;
#[clippy::version = "1.67.0"]
pub UNCHECKED_DURATION_SUBTRACTION,
pedantic,

View file

@ -1,6 +1,7 @@
//! lint on enum variants that are prefixed or suffixed by the same characters
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
use clippy_utils::is_bool;
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};
@ -231,6 +232,10 @@ fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: &
(false, _) => ("pre", prefix),
(true, false) => ("post", postfix),
};
if fields.iter().all(|field| is_bool(field.ty)) {
// If all fields are booleans, we don't want to emit this lint.
return;
}
span_lint_and_help(
cx,
STRUCT_FIELD_NAMES,

View file

@ -5,7 +5,8 @@ use clippy_utils::ty::{implements_trait, make_normalized_projection};
use rustc_ast::Mutability;
use rustc_errors::Applicability;
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_session::declare_lint_pass;
use rustc_span::{sym, Symbol};
@ -152,7 +153,8 @@ fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol
impl LateLintPass<'_> for IterWithoutIntoIter {
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
if let ItemKind::Impl(imp) = item.kind
if !in_external_macro(cx.sess(), item.span)
&& let ItemKind::Impl(imp) = item.kind
&& let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
&& let Some(trait_ref) = imp.of_trait
&& trait_ref
@ -219,7 +221,8 @@ impl {self_ty_without_ref} {{
_ => return,
};
if let ImplItemKind::Fn(sig, _) = item.kind
if !in_external_macro(cx.sess(), item.span)
&& let ImplItemKind::Fn(sig, _) = item.kind
&& let FnRetTy::Return(ret) = sig.decl.output
&& is_nameable_in_impl_trait(ret)
&& cx.tcx.generics_of(item_did).params.is_empty()

View file

@ -115,7 +115,7 @@ mod duplicate_mod;
mod else_if_without_else;
mod empty_drop;
mod empty_enum;
mod empty_structs_with_brackets;
mod empty_with_brackets;
mod endian_bytes;
mod entry;
mod enum_clike;
@ -272,6 +272,7 @@ mod permissions_set_readonly_false;
mod precedence;
mod ptr;
mod ptr_offset_with_cast;
mod pub_underscore_fields;
mod pub_use;
mod question_mark;
mod question_mark_used;
@ -322,6 +323,7 @@ mod swap_ptr_to_ref;
mod tabs_in_doc_comments;
mod temporary_assignment;
mod tests_outside_test_module;
mod thread_local_initializer_can_be_made_const;
mod to_digit_is_some;
mod trailing_empty_array;
mod trait_bounds;
@ -571,6 +573,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
verbose_bit_mask_threshold,
warn_on_all_wildcard_imports,
check_private_items,
pub_underscore_fields_behavior,
blacklisted_names: _,
cyclomatic_complexity_threshold: _,
@ -947,7 +950,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
})
});
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets));
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
store.register_early_pass(|| Box::new(pub_use::PubUse));
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
@ -1080,7 +1083,15 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity));
store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences));
store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions));
store.register_late_pass(|_| Box::new(unconditional_recursion::UnconditionalRecursion));
store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default());
store.register_late_pass(move |_| {
Box::new(pub_underscore_fields::PubUnderscoreFields {
behavior: pub_underscore_fields_behavior,
})
});
store.register_late_pass(move |_| {
Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv()))
});
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -96,8 +96,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo
ExprKind::Path(qpath) => cx
.qpath_res(qpath, fm_arg.hir_id)
.opt_def_id()
.map(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD))
.unwrap_or_default(),
.is_some_and(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)),
// Detect `|x| x.ok()`
ExprKind::Closure(Closure { body, .. }) => {
if let Body {

View file

@ -26,13 +26,14 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) ->
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
let body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(body.value);
let arg_id = body.params[0].pat.hir_id;
match closure_expr.kind {
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
if ident.name == method_name
&& let hir::ExprKind::Path(path) = &receiver.kind
&& let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id)
&& !body.params.is_empty()
{
let arg_id = body.params[0].pat.hir_id;
return arg_id == *local;
}
false

View file

@ -1,80 +1,181 @@
use clippy_utils::ty::get_iterator_item_ty;
use hir::ExprKind;
use rustc_lint::{LateContext, LintContext};
use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline};
use clippy_utils::{is_trait_method, peel_blocks, span_contains_comment};
use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::QPath;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
use std::borrow::Cow;
fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
match &expr.kind {
hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
hir::ExprKind::Path(QPath::Resolved(_, segments)) => {
segments.segments.last().unwrap().ident.name == method_name
///
/// Returns true if the expression is a method call to `method_name`
/// e.g. `a.method_name()` or `Option::method_name`.
///
/// The type-checker verifies for us that the method accepts the right kind of items
/// (e.g. `Option::is_some` accepts `Option<_>`), so we don't need to check that.
///
/// How to capture each case:
///
/// `.filter(|a| { std::option::Option::is_some(a) })`
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a closure, getting unwrapped and
/// recursively checked.
/// `std::option::Option::is_some(a)`
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a call. It unwraps to a path with
/// `QPath::TypeRelative`. Since this is a type relative path, we need to check the method name, the
/// type, and that the parameter of the closure is passed in the call. This part is the dual of
/// `receiver.method_name()` below.
///
/// `filter(std::option::Option::is_some);`
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a type relative path, like above, we check the
/// type and the method name.
///
/// `filter(|a| a.is_some());`
/// ^^^^^^^^^^^^^^^ <- this is a method call inside a closure,
/// we check that the parameter of the closure is the receiver of the method call and don't allow
/// any other parameters.
fn is_method(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
type_symbol: Symbol,
method_name: Symbol,
params: &[&hir::Pat<'_>],
) -> bool {
fn pat_is_recv(ident: Ident, param: &hir::Pat<'_>) -> bool {
match param.kind {
hir::PatKind::Binding(_, _, other, _) => ident == other,
hir::PatKind::Ref(pat, _) => pat_is_recv(ident, pat),
_ => false,
}
}
match expr.kind {
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, recv, ..) => {
// compare the identifier of the receiver to the parameter
// we are in a filter => closure has a single parameter and a single, non-block
// expression, this means that the parameter shadows all outside variables with
// the same name => avoid FPs. If the parameter is not the receiver, then this hits
// outside variables => avoid FP
if ident.name == method_name
&& let ExprKind::Path(QPath::Resolved(None, path)) = recv.kind
&& let &[seg] = path.segments
&& params.iter().any(|p| pat_is_recv(seg.ident, p))
{
return true;
}
false
},
// This is used to check for complete paths via `|a| std::option::Option::is_some(a)`
// this then unwraps to a path with `QPath::TypeRelative`
// we pass the params as they've been passed to the current call through the closure
hir::ExprKind::Call(expr, [param]) => {
// this will hit the `QPath::TypeRelative` case and check that the method name is correct
if is_method(cx, expr, type_symbol, method_name, params)
// we then check that this is indeed passing the parameter of the closure
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.kind
&& let &[seg] = path.segments
&& params.iter().any(|p| pat_is_recv(seg.ident, p))
{
return true;
}
false
},
hir::ExprKind::Path(QPath::TypeRelative(ty, mname)) => {
let ty = cx.typeck_results().node_type(ty.hir_id);
if let Some(did) = cx.tcx.get_diagnostic_item(type_symbol)
&& ty.ty_adt_def() == cx.tcx.type_of(did).skip_binder().ty_adt_def()
{
return mname.ident.name == method_name;
}
false
},
hir::ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name,
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
let body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(body.value);
let arg_id = body.params[0].pat.hir_id;
match closure_expr.kind {
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
if ident.name == method_name
&& let hir::ExprKind::Path(path) = &receiver.kind
&& let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id)
{
return arg_id == *local;
}
false
},
_ => false,
}
let params = body.params.iter().map(|param| param.pat).collect::<Vec<_>>();
is_method(cx, closure_expr, type_symbol, method_name, params.as_slice())
},
_ => false,
}
}
fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
if let hir::Node::Expr(parent_expr) = cx.tcx.hir().get_parent(expr.hir_id) {
is_method(cx, parent_expr, rustc_span::sym::map)
} else {
false
if let Some(expr) = get_parent_expr(cx, expr)
&& is_trait_method(cx, expr, sym::Iterator)
&& let hir::ExprKind::MethodCall(path, _, _, _) = expr.kind
&& path.ident.name == rustc_span::sym::map
{
return true;
}
false
}
#[allow(clippy::too_many_arguments)]
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, filter_span: Span) {
let is_iterator = is_trait_method(cx, expr, sym::Iterator);
let parent_is_not_map = !parent_is_map(cx, expr);
enum FilterType {
IsSome,
IsOk,
}
if is_iterator
&& parent_is_not_map
&& is_method(cx, filter_arg, sym!(is_some))
&& !span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi()))
/// Returns the `FilterType` of the expression if it is a filter over an Iter<Option> or
/// Iter<Result> with the parent expression not being a map, and not having a comment in the span of
/// the filter. If it is not a filter over an Iter<Option> or Iter<Result> then it returns None
///
/// How this is done:
/// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs`
/// 2. we check that we are in a trait method. Therefore we are in an
/// `(x as Iterator).filter({filter_arg})` method call.
/// 3. we check that the parent expression is not a map. This is because we don't want to lint
/// twice, and we already have a specialized lint for that.
/// 4. we check that the span of the filter does not contain a comment.
/// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and
/// Result.
/// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or
/// `is_ok`.
/// 7. if all of the above are true, then we return the `FilterType`
fn expression_type(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
filter_arg: &hir::Expr<'_>,
filter_span: Span,
) -> Option<FilterType> {
if !is_trait_method(cx, expr, sym::Iterator)
|| parent_is_map(cx, expr)
|| span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi()))
{
span_lint_and_sugg(
cx,
ITER_FILTER_IS_SOME,
filter_span.with_hi(expr.span.hi()),
"`filter` for `is_some` on iterator over `Option`",
"consider using `flatten` instead",
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
Applicability::HasPlaceholders,
);
return None;
}
if is_iterator
&& parent_is_not_map
&& is_method(cx, filter_arg, sym!(is_ok))
&& !span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi()))
if let hir::ExprKind::MethodCall(_, receiver, _, _) = expr.kind
&& let receiver_ty = cx.typeck_results().expr_ty(receiver)
&& let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty)
{
span_lint_and_sugg(
if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Option)
&& let opt_ty = cx.tcx.type_of(opt_defid).skip_binder()
&& iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def()
&& is_method(cx, filter_arg, sym::Option, sym!(is_some), &[])
{
return Some(FilterType::IsSome);
}
if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Result)
&& let opt_ty = cx.tcx.type_of(opt_defid).skip_binder()
&& iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def()
&& is_method(cx, filter_arg, sym::Result, sym!(is_ok), &[])
{
return Some(FilterType::IsOk);
}
}
None
}
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, filter_span: Span) {
// we are in a filter inside an iterator
match expression_type(cx, expr, filter_arg, filter_span) {
None => (),
Some(FilterType::IsOk) => span_lint_and_sugg(
cx,
ITER_FILTER_IS_OK,
filter_span.with_hi(expr.span.hi()),
@ -82,6 +183,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir
"consider using `flatten` instead",
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
Applicability::HasPlaceholders,
);
),
Some(FilterType::IsSome) => span_lint_and_sugg(
cx,
ITER_FILTER_IS_SOME,
filter_span.with_hi(expr.span.hi()),
"`filter` for `is_some` on iterator over `Option`",
"consider using `flatten` instead",
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
Applicability::HasPlaceholders,
),
}
}

View file

@ -0,0 +1,59 @@
use clippy_config::msrvs::{Msrv, OPTION_RESULT_IS_VARIANT_AND};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::{sym, Span};
use super::MANUAL_IS_VARIANT_AND;
pub(super) fn check<'tcx>(
cx: &LateContext<'_>,
expr: &'tcx rustc_hir::Expr<'_>,
map_recv: &'tcx rustc_hir::Expr<'_>,
map_arg: &'tcx rustc_hir::Expr<'_>,
map_span: Span,
msrv: &Msrv,
) {
// Don't lint if:
// 1. the `expr` is generated by a macro
if expr.span.from_expansion() {
return;
}
// 2. the caller of `map()` is neither `Option` nor `Result`
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option);
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result);
if !is_option && !is_result {
return;
}
// 3. the caller of `unwrap_or_default` is neither `Option<bool>` nor `Result<bool, _>`
if !cx.typeck_results().expr_ty(expr).is_bool() {
return;
}
// 4. msrv doesn't meet `OPTION_RESULT_IS_VARIANT_AND`
if !msrv.meets(OPTION_RESULT_IS_VARIANT_AND) {
return;
}
let lint_msg = if is_option {
"called `map(<f>).unwrap_or_default()` on an `Option` value"
} else {
"called `map(<f>).unwrap_or_default()` on a `Result` value"
};
let suggestion = if is_option { "is_some_and" } else { "is_ok_and" };
span_lint_and_sugg(
cx,
MANUAL_IS_VARIANT_AND,
expr.span.with_lo(map_span.lo()),
lint_msg,
"use",
format!("{}({})", suggestion, snippet(cx, map_arg.span, "..")),
Applicability::MachineApplicable,
);
}

View file

@ -2,9 +2,10 @@ use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use clippy_utils::{is_diag_trait_item, peel_blocks};
use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::ty;
@ -14,60 +15,110 @@ use rustc_span::{sym, Span};
use super::MAP_CLONE;
// If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't
// run this lint because it would overlap with `useless_asref` which provides a better suggestion
// in this case.
fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool {
if is_diag_trait_item(cx, method_id, sym::Iterator) {
return true;
}
// We check if it's an `Option` or a `Result`.
if let Some(id) = cx.tcx.impl_of_method(method_id) {
let identity = cx.tcx.type_of(id).instantiate_identity();
if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) {
return false;
}
} else {
return false;
}
// We check if the previous method call is `as_ref`.
if let hir::ExprKind::MethodCall(path1, receiver, _, _) = &e.kind
&& let hir::ExprKind::MethodCall(path2, _, _, _) = &receiver.kind
{
return path2.ident.name != sym::as_ref || path1.ident.name != sym::map;
}
true
}
pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& (cx.tcx.impl_of_method(method_id).map_or(false, |id| {
is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option)
}) || is_diag_trait_item(cx, method_id, sym::Iterator))
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind
&& should_run_lint(cx, e, method_id)
{
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, hir::Mutability::Not) => {
if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind {
if ident_eq(name, closure_expr) {
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
}
},
hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => {
match closure_expr.kind {
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
if ident_eq(name, inner) {
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
match arg.kind {
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, hir::Mutability::Not) => {
if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind {
if ident_eq(name, closure_expr) {
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
}
},
hir::ExprKind::MethodCall(method, obj, [], _) => {
if ident_eq(name, obj) && method.ident.name == sym::clone
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
// no autoderefs
&& !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
{
let obj_ty = cx.typeck_results().expr_ty(obj);
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
if matches!(mutability, Mutability::Not) {
let copy = is_copy(cx, *ty);
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => {
match closure_expr.kind {
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
if ident_eq(name, inner) {
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
}
} else {
lint_needless_cloning(cx, e.span, recv.span);
}
},
hir::ExprKind::MethodCall(method, obj, [], _) => {
if ident_eq(name, obj) && method.ident.name == sym::clone
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
// no autoderefs
&& !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
{
let obj_ty = cx.typeck_results().expr_ty(obj);
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
if matches!(mutability, Mutability::Not) {
let copy = is_copy(cx, *ty);
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
}
} else {
lint_needless_cloning(cx, e.span, recv.span);
}
}
},
hir::ExprKind::Call(call, [_]) => {
if let hir::ExprKind::Path(qpath) = call.kind {
handle_path(cx, call, &qpath, e, recv);
}
},
_ => {},
}
},
_ => {},
}
},
hir::ExprKind::Path(qpath) => handle_path(cx, arg, &qpath, e, recv),
_ => {},
}
}
}
fn handle_path(
cx: &LateContext<'_>,
arg: &hir::Expr<'_>,
qpath: &hir::QPath<'_>,
e: &hir::Expr<'_>,
recv: &hir::Expr<'_>,
) {
if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
&& match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD)
{
// FIXME: It would be better to infer the type to check if it's copyable or not
// to suggest to use `.copied()` instead of `.cloned()` where applicable.
lint_path(cx, e.span, recv.span);
}
}
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
path.segments.len() == 1 && path.segments[0].ident == name
@ -88,6 +139,23 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
);
}
fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
MAP_CLONE,
replace,
"you are explicitly cloning with `.map()`",
"consider calling the dedicated `cloned` method",
format!(
"{}.cloned()",
snippet_with_applicability(cx, root, "..", &mut applicability),
),
applicability,
);
}
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
let mut applicability = Applicability::MachineApplicable;

View file

@ -51,6 +51,7 @@ mod iter_skip_zero;
mod iter_with_drain;
mod iterator_step_by_zero;
mod join_absolute_paths;
mod manual_is_variant_and;
mod manual_next_back;
mod manual_ok_or;
mod manual_saturating_arithmetic;
@ -70,6 +71,7 @@ mod no_effect_replace;
mod obfuscated_if_else;
mod ok_expect;
mod open_options;
mod option_as_ref_cloned;
mod option_as_ref_deref;
mod option_map_or_err_ok;
mod option_map_or_none;
@ -93,6 +95,7 @@ mod single_char_pattern;
mod single_char_push_string;
mod skip_while_next;
mod stable_sort_primitive;
mod str_split;
mod str_splitn;
mod string_extend_chars;
mod string_lit_chars_any;
@ -2589,11 +2592,11 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `x.get(0)` instead of
/// `x.first()`.
/// `x.first()` or `x.front()`.
///
/// ### Why is this bad?
/// Using `x.first()` is easier to read and has the same
/// result.
/// Using `x.first()` for `Vec`s and slices or `x.front()`
/// for `VecDeque`s is easier to read and has the same result.
///
/// ### Example
/// ```no_run
@ -2609,7 +2612,7 @@ declare_clippy_lint! {
#[clippy::version = "1.63.0"]
pub GET_FIRST,
style,
"Using `x.get(0)` when `x.first()` is simpler"
"Using `x.get(0)` when `x.first()` or `x.front()` is simpler"
}
declare_clippy_lint! {
@ -3784,7 +3787,7 @@ declare_clippy_lint! {
///
/// ### Why is this bad?
/// This pattern is often followed by manual unwrapping of the `Option`. The simplification
/// results in more readable and succint code without the need for manual unwrapping.
/// results in more readable and succinct code without the need for manual unwrapping.
///
/// ### Example
/// ```no_run
@ -3810,7 +3813,7 @@ declare_clippy_lint! {
///
/// ### Why is this bad?
/// This pattern is often followed by manual unwrapping of `Result`. The simplification
/// results in more readable and succint code without the need for manual unwrapping.
/// results in more readable and succinct code without the need for manual unwrapping.
///
/// ### Example
/// ```no_run
@ -3829,6 +3832,87 @@ declare_clippy_lint! {
"filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`"
}
declare_clippy_lint! {
/// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where f is a function or closure that returns the `bool` type.
///
/// ### Why is this bad?
/// Readability. These can be written more concisely as `option.is_some_and(f)` and `result.is_ok_and(f)`.
///
/// ### Example
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.map(|a| a > 10).unwrap_or_default();
/// result.map(|a| a > 10).unwrap_or_default();
/// ```
/// Use instead:
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.is_some_and(|a| a > 10);
/// result.is_ok_and(|a| a > 10);
/// ```
#[clippy::version = "1.76.0"]
pub MANUAL_IS_VARIANT_AND,
pedantic,
"using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for usages of `str.trim().split("\n")` and `str.trim().split("\r\n")`.
///
/// ### Why is this bad?
///
/// Hard-coding the line endings makes the code less compatible. `str.lines` should be used instead.
///
/// ### Example
/// ```no_run
/// "some\ntext\nwith\nnewlines\n".trim().split('\n');
/// ```
/// Use instead:
/// ```no_run
/// "some\ntext\nwith\nnewlines\n".lines();
/// ```
///
/// ### Known Problems
///
/// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or
/// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is
/// valid.
/// ```
#[clippy::version = "1.76.0"]
pub STR_SPLIT_AT_NEWLINE,
pedantic,
"splitting a trimmed string at hard-coded newlines"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.as_ref().cloned()` and `.as_mut().cloned()` on `Option`s
///
/// ### Why is this bad?
/// This can be written more concisely by cloning the `Option` directly.
///
/// ### Example
/// ```no_run
/// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
/// bar.as_ref().cloned()
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
/// bar.clone()
/// }
/// ```
#[clippy::version = "1.77.0"]
pub OPTION_AS_REF_CLONED,
pedantic,
"cloning an `Option` via `as_ref().cloned()`"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -3983,6 +4067,9 @@ impl_lint_pass!(Methods => [
RESULT_FILTER_MAP,
ITER_FILTER_IS_SOME,
ITER_FILTER_IS_OK,
MANUAL_IS_VARIANT_AND,
STR_SPLIT_AT_NEWLINE,
OPTION_AS_REF_CLONED,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4230,7 +4317,10 @@ impl Methods {
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv),
("cloned", []) => {
cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv);
option_as_ref_cloned::check(cx, recv, span);
},
("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
needless_collect::check(cx, span, expr, recv, call_span);
match method_call(recv) {
@ -4569,6 +4659,9 @@ impl Methods {
("sort_unstable_by", [arg]) => {
unnecessary_sort_by::check(cx, expr, recv, arg, true);
},
("split", [arg]) => {
str_split::check(cx, expr, recv, arg);
},
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
@ -4664,7 +4757,13 @@ impl Methods {
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
("unwrap_or_default", []) => {
if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) {
manual_is_variant_and::check(cx, expr, m_recv, arg, span, &self.msrv);
}
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]) => {

View file

@ -0,0 +1,24 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_span::{sym, Span};
use super::{method_call, OPTION_AS_REF_CLONED};
pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) {
if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv)
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option)
{
span_lint_and_sugg(
cx,
OPTION_AS_REF_CLONED,
as_ref_ident_span.to(cloned_ident_span),
&format!("cloning an `Option<_>` using `.{method}().cloned()`"),
"this can be written more concisely by cloning the `Option<_>` directly",
"clone".into(),
Applicability::MachineApplicable,
);
}
}

View file

@ -72,7 +72,7 @@ pub(super) fn check<'tcx>(
}
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
let suggest_is_some_and = msrv.meets(msrvs::OPTION_IS_SOME_AND)
let suggest_is_some_and = msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
&& matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
if matches!(lit.node, rustc_ast::LitKind::Bool(false)));

View file

@ -33,7 +33,7 @@ pub(super) fn check(
&& path.chars().all(char::is_alphanumeric)
{
let mut sugg = snippet(cx, recv.span, "..").into_owned();
if msrv.meets(msrvs::OPTION_IS_SOME_AND) {
if msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) {
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
} else {
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);

View file

@ -13,7 +13,11 @@ pub(super) fn check(
as_str_span: Span,
other_method_span: Span,
) {
if cx.typeck_results().expr_ty(recv).ty_adt_def().is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string())
if cx
.typeck_results()
.expr_ty(recv)
.ty_adt_def()
.is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string())
{
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(

View file

@ -0,0 +1,38 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context;
use clippy_utils::visitors::is_const_evaluatable;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use super::STR_SPLIT_AT_NEWLINE;
pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &'a Expr<'_>, split_arg: &'_ Expr<'_>) {
// We're looking for `A.trim().split(B)`, where the adjusted type of `A` is `&str` (e.g. an
// expression returning `String`), and `B` is a `Pattern` that hard-codes a newline (either `"\n"`
// or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most
// basic ones: a `'\n'`, `"\n"`, and `"\r\n"`.
if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind
&& trim_method_name.ident.as_str() == "trim"
&& cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str()
&& !is_const_evaluatable(cx, trim_recv)
&& let ExprKind::Lit(split_lit) = split_arg.kind
&& (matches!(split_lit.node, LitKind::Char('\n'))
|| matches!(split_lit.node, LitKind::Str(sym, _) if (sym.as_str() == "\n" || sym.as_str() == "\r\n")))
{
let mut app = Applicability::MaybeIncorrect;
span_lint_and_sugg(
cx,
STR_SPLIT_AT_NEWLINE,
expr.span,
"using `str.trim().split()` with hard-coded newlines",
"use `str.lines()` instead",
format!(
"{}.lines()",
snippet_with_context(cx, trim_recv.span, expr.span.ctxt(), "..", &mut app).0
),
app,
);
}
}

View file

@ -3,13 +3,13 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_mid_ty_refs};
use clippy_utils::visitors::find_all_ret_expressions;
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node};
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node};
use rustc_hir_typeck::{FnCtxt, Inherited};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
@ -246,6 +246,19 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
&& let Some(arg_snippet) = snippet_opt(cx, argument_expr.span)
{
// We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x`
// implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to
// add `.as_ref()` to the suggestion.
let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String)
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
&& cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, "Target")
!= Some(cx.tcx.types.str_)
{
".as_ref()"
} else {
""
};
// The next suggestion may be incorrect because the removal of the `to_owned`-like
// function could cause the iterator to hold a reference to a resource that is used
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
@ -255,7 +268,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
parent.span,
&format!("unnecessary use of `{method_name}`"),
"use",
format!("{receiver_snippet}.split({arg_snippet})"),
format!("{receiver_snippet}{as_ref}.split({arg_snippet})"),
Applicability::MaybeIncorrect,
);
return true;
@ -445,7 +458,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
{
let bound_fn_sig = cx.tcx.fn_sig(callee_def_id);
let fn_sig = bound_fn_sig.skip_binder();
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
if let Some(arg_index) = recv
.into_iter()
.chain(call_args)
.position(|arg| arg.hir_id == expr.hir_id)
&& let param_ty = fn_sig.input(arg_index).skip_binder()
&& let ty::Param(ParamTy { index: param_index , ..}) = *param_ty.kind()
// https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021

View file

@ -1,19 +1,52 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::walk_ptrs_ty_depth;
use clippy_utils::{get_parent_expr, is_trait_method};
use clippy_utils::{get_parent_expr, is_diag_trait_item, match_def_path, paths, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_span::{sym, Span};
use core::ops::ControlFlow;
use super::USELESS_ASREF;
/// Returns the first type inside the `Option`/`Result` type passed as argument.
fn get_enum_ty(enum_ty: Ty<'_>) -> Option<Ty<'_>> {
struct ContainsTyVisitor {
level: usize,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTyVisitor {
type BreakTy = Ty<'tcx>;
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
self.level += 1;
if self.level == 1 {
t.super_visit_with(self)
} else {
ControlFlow::Break(t)
}
}
}
match enum_ty.visit_with(&mut ContainsTyVisitor { level: 0 }) {
ControlFlow::Break(ty) => Some(ty),
ControlFlow::Continue(()) => None,
}
}
/// Checks for the `USELESS_ASREF` lint.
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) {
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
// check if the call is to the actual `AsRef` or `AsMut` trait
if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, sym::AsMut) {
let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else {
return;
};
if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) {
// check if the type after `as_ref` or `as_mut` is the same as before
let rcv_ty = cx.typeck_results().expr_ty(recvr);
let res_ty = cx.typeck_results().expr_ty(expr);
@ -39,5 +72,89 @@ 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])
{
let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs();
let res_ty = cx.typeck_results().expr_ty(expr).peel_refs();
if let Some(rcv_ty) = get_enum_ty(rcv_ty)
&& let Some(res_ty) = get_enum_ty(res_ty)
// If the only thing the `as_mut`/`as_ref` call is doing is adding references and not
// changing the type, then we can move forward.
&& rcv_ty.peel_refs() == res_ty.peel_refs()
&& let Some(parent) = get_parent_expr(cx, expr)
&& let hir::ExprKind::MethodCall(segment, _, args, _) = parent.kind
&& segment.ident.span != expr.span
// We check that the called method name is `map`.
&& segment.ident.name == sym::map
// And that it only has one argument.
&& let [arg] = args
&& is_calling_clone(cx, arg)
{
lint_as_ref_clone(cx, expr.span.with_hi(parent.span.hi()), recvr, call_name);
}
}
}
fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) -> bool {
// We check it's calling the `clone` method of the `Clone` trait.
if let Some(path_def_id) = cx.qpath_res(&qpath, hir_id).opt_def_id() {
match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD)
} else {
false
}
}
fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
match arg.kind {
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
// If it's a closure, we need to check what is called.
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(closure_body.value);
match closure_expr.kind {
hir::ExprKind::MethodCall(method, obj, [], _) => {
if method.ident.name == sym::clone
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
// We check it's the `Clone` trait.
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
// no autoderefs
&& !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
{
true
} else {
false
}
},
hir::ExprKind::Call(call, [_]) => {
if let hir::ExprKind::Path(qpath) = call.kind {
check_qpath(cx, qpath, call.hir_id)
} else {
false
}
},
_ => false,
}
},
hir::ExprKind::Path(qpath) => check_qpath(cx, qpath, arg.hir_id),
_ => false,
}
}
fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: &str) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
USELESS_ASREF,
span,
&format!("this call to `{call_name}.map(...)` does nothing"),
"try",
format!(
"{}.clone()",
snippet_with_applicability(cx, recvr.span, "..", &mut applicability)
),
applicability,
);
}

View file

@ -74,6 +74,7 @@ pub(super) fn get_hint_if_single_char_arg(
match ch {
"'" => "\\'",
r"\" => "\\\\",
"\\\"" => "\"", // no need to escape `"` in `'"'`
_ => ch,
}
);

View file

@ -331,7 +331,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
slice,
} if indexes.len() > 1 => {
// if we have found an `assert!`, let's also check that it's actually right
// and if it convers the highest index and if not, suggest the correct length
// and if it covers the highest index and if not, suggest the correct length
let sugg = match comparison {
// `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks.
// The user probably meant `v.len() > 5`

View file

@ -13,21 +13,23 @@ use rustc_span::Symbol;
declare_clippy_lint! {
/// ### What it does
/// Checks for imports that do not rename the item as specified
/// in the `enforce-import-renames` config option.
/// in the `enforced-import-renames` config option.
///
/// Note: Even though this lint is warn-by-default, it will only trigger if
/// import renames are defined in the clippy.toml file.
/// import renames are defined in the `clippy.toml` file.
///
/// ### Why is this bad?
/// Consistency is important, if a project has defined import
/// renames they should be followed. More practically, some item names are too
/// vague outside of their defining scope this can enforce a more meaningful naming.
/// Consistency is important; if a project has defined import renames, then they should be
/// followed. More practically, some item names are too vague outside of their defining scope,
/// in which case this can enforce a more meaningful naming.
///
/// ### Example
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }]
/// enforced-import-renames = [
/// { path = "serde_json::Value", rename = "JsonValue" },
/// ]
/// ```
///
/// ```rust,ignore

View file

@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, IntTy, Ty, UintTy};
use rustc_session::declare_lint_pass;
use rustc_span::sym;
@ -105,8 +105,28 @@ impl<'tcx> LateLintPass<'tcx> for Mutex {
fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
match ty.kind() {
ty::Bool => Some("AtomicBool"),
ty::Uint(_) => Some("AtomicUsize"),
ty::Int(_) => Some("AtomicIsize"),
ty::Uint(uint_ty) => {
match uint_ty {
UintTy::U8 => Some("AtomicU8"),
UintTy::U16 => Some("AtomicU16"),
UintTy::U32 => Some("AtomicU32"),
UintTy::U64 => Some("AtomicU64"),
UintTy::Usize => Some("AtomicUsize"),
// There's no `AtomicU128`.
UintTy::U128 => None,
}
},
ty::Int(int_ty) => {
match int_ty {
IntTy::I8 => Some("AtomicI8"),
IntTy::I16 => Some("AtomicI16"),
IntTy::I32 => Some("AtomicI32"),
IntTy::I64 => Some("AtomicI64"),
IntTy::Isize => Some("AtomicIsize"),
// There's no `AtomicI128`.
IntTy::I128 => None,
}
},
ty::RawPtr(_) => Some("AtomicPtr"),
_ => None,
}

View file

@ -165,7 +165,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
}
fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
// It's very hard or impossable to check whether overridden operator have side-effect this lint.
// It's very hard or impossible to check whether overridden operator have side-effect this lint.
// So, this function assume user-defined operator is overridden with an side-effect.
// The definition of user-defined structure here is ADT-type,
// Althrough this will weaken the ability of this lint, less error lint-fix happen.

View file

@ -53,14 +53,10 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
&& let ExprKind::Lit(_) = param.kind
&& param.span.eq_ctxt(expr.span)
&& let Some(snip) = snippet_opt(cx, param.span)
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
{
let Some(snip) = snippet_opt(cx, param.span) else {
return;
};
if !snip.starts_with("0o") {
show_error(cx, param);
}
show_error(cx, param);
}
},
ExprKind::Call(func, [param]) => {
@ -70,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
&& let ExprKind::Lit(_) = param.kind
&& param.span.eq_ctxt(expr.span)
&& let Some(snip) = snippet_opt(cx, param.span)
&& !snip.starts_with("0o")
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
{
show_error(cx, param);
}

View file

@ -18,82 +18,118 @@ pub(crate) fn check<'tcx>(
right: &'tcx Expr<'_>,
) {
if !is_allowed(cx, op, left, right) {
match op {
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
check_op(
cx,
left,
0,
expr.span,
peel_hir_expr_refs(right).0.span,
needs_parenthesis(cx, expr, right),
);
check_op(
cx,
right,
0,
expr.span,
peel_hir_expr_refs(left).0.span,
Parens::Unneeded,
);
},
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
check_op(
cx,
right,
0,
expr.span,
peel_hir_expr_refs(left).0.span,
Parens::Unneeded,
);
},
BinOpKind::Mul => {
check_op(
cx,
left,
1,
expr.span,
peel_hir_expr_refs(right).0.span,
needs_parenthesis(cx, expr, right),
);
check_op(
cx,
right,
1,
expr.span,
peel_hir_expr_refs(left).0.span,
Parens::Unneeded,
);
},
BinOpKind::Div => check_op(
return;
}
// we need to know whether a ref is coerced to a value
// if a ref is coerced, then the suggested lint must deref it
// e.g. `let _: i32 = x+0` with `x: &i32` should be replaced with `let _: i32 = *x`.
// we do this by checking the _kind_ of the type of the expression
// if it's a ref, we then check whether it is erased, and that's it.
let (peeled_left_span, left_is_coerced_to_value) = {
let expr = peel_hir_expr_refs(left).0;
let span = expr.span;
let is_coerced = expr_is_erased_ref(cx, expr);
(span, is_coerced)
};
let (peeled_right_span, right_is_coerced_to_value) = {
let expr = peel_hir_expr_refs(right).0;
let span = expr.span;
let is_coerced = expr_is_erased_ref(cx, expr);
(span, is_coerced)
};
match op {
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
check_op(
cx,
left,
0,
expr.span,
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
);
check_op(
cx,
right,
0,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
},
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
check_op(
cx,
right,
0,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
},
BinOpKind::Mul => {
check_op(
cx,
left,
1,
expr.span,
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
);
check_op(
cx,
right,
1,
expr.span,
peel_hir_expr_refs(left).0.span,
peeled_left_span,
Parens::Unneeded,
),
BinOpKind::BitAnd => {
check_op(
cx,
left,
-1,
expr.span,
peel_hir_expr_refs(right).0.span,
needs_parenthesis(cx, expr, right),
);
check_op(
cx,
right,
-1,
expr.span,
peel_hir_expr_refs(left).0.span,
Parens::Unneeded,
);
},
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
_ => (),
}
left_is_coerced_to_value,
);
},
BinOpKind::Div => check_op(
cx,
right,
1,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
),
BinOpKind::BitAnd => {
check_op(
cx,
left,
-1,
expr.span,
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
);
check_op(
cx,
right,
-1,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
},
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
_ => (),
}
}
fn expr_is_erased_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match cx.typeck_results().expr_ty(expr).kind() {
ty::Ref(r, ..) => r.is_erased(),
_ => false,
}
}
@ -144,11 +180,11 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
}
fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
// This lint applies to integers
!cx.typeck_results().expr_ty(left).peel_refs().is_integral()
|| !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
// This lint applies to integers and their references
cx.typeck_results().expr_ty(left).peel_refs().is_integral()
&& cx.typeck_results().expr_ty(right).peel_refs().is_integral()
// `1 << 0` is a common pattern in bit manipulation code
|| (cmp == BinOpKind::Shl
&& !(cmp == BinOpKind::Shl
&& constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
}
@ -161,11 +197,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
(Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
_ => return,
} {
span_ineffective_operation(cx, span, arg, Parens::Unneeded);
span_ineffective_operation(cx, span, arg, Parens::Unneeded, false);
}
}
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) {
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
@ -178,18 +214,28 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
1 => v == 1,
_ => unreachable!(),
} {
span_ineffective_operation(cx, span, arg, parens);
span_ineffective_operation(cx, span, arg, parens, is_erased);
}
}
}
fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) {
fn span_ineffective_operation(
cx: &LateContext<'_>,
span: Span,
arg: Span,
parens: Parens,
is_ref_coerced_to_val: bool,
) {
let mut applicability = Applicability::MachineApplicable;
let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability);
let expr_snippet = if is_ref_coerced_to_val {
format!("*{expr_snippet}")
} else {
expr_snippet.into_owned()
};
let suggestion = match parens {
Parens::Needed => format!("({expr_snippet})"),
Parens::Unneeded => expr_snippet.into_owned(),
Parens::Unneeded => expr_snippet,
};
span_lint_and_sugg(

View file

@ -0,0 +1,83 @@
use clippy_config::types::PubUnderscoreFieldsBehaviour;
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_path_lang_item;
use rustc_hir::{FieldDef, Item, ItemKind, LangItem};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks whether any field of the struct is prefixed with an `_` (underscore) and also marked
/// `pub` (public)
///
/// ### Why is this bad?
/// Fields prefixed with an `_` are inferred as unused, which suggests it should not be marked
/// as `pub`, because marking it as `pub` infers it will be used.
///
/// ### Example
/// ```rust
/// struct FileHandle {
/// pub _descriptor: usize,
/// }
/// ```
/// Use instead:
/// ```rust
/// struct FileHandle {
/// _descriptor: usize,
/// }
/// ```
///
/// OR
///
/// ```rust
/// struct FileHandle {
/// pub descriptor: usize,
/// }
/// ```
#[clippy::version = "1.77.0"]
pub PUB_UNDERSCORE_FIELDS,
pedantic,
"struct field prefixed with underscore and marked public"
}
pub struct PubUnderscoreFields {
pub behavior: PubUnderscoreFieldsBehaviour,
}
impl_lint_pass!(PubUnderscoreFields => [PUB_UNDERSCORE_FIELDS]);
impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
// This lint only pertains to structs.
let ItemKind::Struct(variant_data, _) = &item.kind else {
return;
};
let is_visible = |field: &FieldDef<'_>| match self.behavior {
PubUnderscoreFieldsBehaviour::PublicallyExported => cx.effective_visibilities.is_reachable(field.def_id),
PubUnderscoreFieldsBehaviour::AllPubFields => {
// If there is a visibility span then the field is marked pub in some way.
!field.vis_span.is_empty()
},
};
for field in variant_data.fields() {
// Only pertains to fields that start with an underscore, and are public.
if field.ident.as_str().starts_with('_') && is_visible(field)
// We ignore fields that have `#[doc(hidden)]`.
&& !is_doc_hidden(cx.tcx.hir().attrs(field.hir_id))
// We ignore fields that are `PhantomData`.
&& !is_path_lang_item(cx, field.ty, LangItem::PhantomData)
{
span_lint_and_help(
cx,
PUB_UNDERSCORE_FIELDS,
field.vis_span.to(field.ident.span),
"field marked as public but also inferred as unused because it's prefixed with `_`",
None,
"consider removing the underscore, or making the field private",
);
}
}
}
}

View file

@ -75,12 +75,7 @@ enum IfBlockType<'hir> {
/// An `if x.is_xxx() { a } else { b } ` expression.
///
/// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
IfIs(
&'hir Expr<'hir>,
Ty<'hir>,
Symbol,
&'hir Expr<'hir>,
),
IfIs(&'hir Expr<'hir>, Ty<'hir>, Symbol, &'hir Expr<'hir>),
/// An `if let Xxx(a) = b { c } else { d }` expression.
///
/// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),

View file

@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for mis-uses of the serde API.
/// Checks for misuses of the serde API.
///
/// ### Why is this bad?
/// Serde is very finnicky about how its API should be

View file

@ -0,0 +1,102 @@
use clippy_config::msrvs::Msrv;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::fn_has_unsatisfiable_preds;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir::{intravisit, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass;
use rustc_span::sym::thread_local_macro;
declare_clippy_lint! {
/// ### What it does
/// Suggests to use `const` in `thread_local!` macro if possible.
/// ### Why is this bad?
///
/// The `thread_local!` macro wraps static declarations and makes them thread-local.
/// It supports using a `const` keyword that may be used for declarations that can
/// be evaluated as a constant expression. This can enable a more efficient thread
/// local implementation that can avoid lazy initialization. For types that do not
/// need to be dropped, this can enable an even more efficient implementation that
/// does not need to track any additional state.
///
/// https://doc.rust-lang.org/std/macro.thread_local.html
///
/// ### Example
/// ```no_run
/// // example code where clippy issues a warning
/// thread_local! {
/// static BUF: String = String::new();
/// }
/// ```
/// Use instead:
/// ```no_run
/// // example code which does not raise clippy warning
/// thread_local! {
/// static BUF: String = const { String::new() };
/// }
/// ```
#[clippy::version = "1.76.0"]
pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
perf,
"suggest using `const` in `thread_local!` macro"
}
pub struct ThreadLocalInitializerCanBeMadeConst {
msrv: Msrv,
}
impl ThreadLocalInitializerCanBeMadeConst {
#[must_use]
pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST]);
impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
fn_kind: rustc_hir::intravisit::FnKind<'tcx>,
_: &'tcx rustc_hir::FnDecl<'tcx>,
body: &'tcx rustc_hir::Body<'tcx>,
span: rustc_span::Span,
defid: rustc_span::def_id::LocalDefId,
) {
if in_external_macro(cx.sess(), span)
&& let Some(callee) = span.source_callee()
&& let Some(macro_def_id) = callee.macro_def_id
&& cx.tcx.is_diagnostic_item(thread_local_macro, macro_def_id)
&& let intravisit::FnKind::ItemFn(..) = fn_kind
// Building MIR for `fn`s with unsatisfiable preds results in ICE.
&& !fn_has_unsatisfiable_preds(cx, defid.to_def_id())
&& let mir = cx.tcx.optimized_mir(defid.to_def_id())
&& let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv)
// this is the `__init` function emitted by the `thread_local!` macro
// when the `const` keyword is not used. We avoid checking the `__init` directly
// as that is not a public API.
// we know that the function is const-qualifiable, so now we need only to get the
// initializer expression to span-lint it.
&& let ExprKind::Block(block, _) = body.value.kind
&& let Some(ret_expr) = block.expr
&& let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }")
&& initializer_snippet != "thread_local! { ... }"
{
span_lint_and_sugg(
cx,
THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
ret_expr.span,
"initializer for `thread_local` value can be made `const`",
"replace with",
format!("const {{ {initializer_snippet} }}"),
Applicability::MachineApplicable,
);
}
}
extract_msrv_attr!(LateContext);
}

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_normalizable;
use clippy_utils::{path_to_local, path_to_local_id};
use clippy_utils::{eq_expr_value, path_to_local};
use rustc_abi::WrappingRange;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node};
@ -25,6 +25,52 @@ fn range_fully_contained(from: WrappingRange, to: WrappingRange) -> bool {
to.contains(from.start) && to.contains(from.end)
}
/// Checks if a given expression is a binary operation involving a local variable or is made up of
/// other (nested) binary expressions involving the local. There must be at least one local
/// reference that is the same as `local_expr`.
///
/// This is used as a heuristic to detect if a variable
/// is checked to be within the valid range of a transmuted type.
/// All of these would return true:
/// * `x < 4`
/// * `x < 4 && x > 1`
/// * `x.field < 4 && x.field > 1` (given `x.field`)
/// * `x.field < 4 && unrelated()`
/// * `(1..=3).contains(&x)`
fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Binary(_, lhs, rhs) => {
binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs)
},
ExprKind::MethodCall(path, receiver, [arg], _)
if path.ident.name == sym!(contains)
// ... `contains` called on some kind of range
&& let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def()
&& let lang_items = cx.tcx.lang_items()
&& [
lang_items.range_from_struct(),
lang_items.range_inclusive_struct(),
lang_items.range_struct(),
lang_items.range_to_inclusive_struct(),
lang_items.range_to_struct()
].into_iter().any(|did| did == Some(receiver_adt.did())) =>
{
eq_expr_value(cx, local_expr, arg.peel_borrows())
},
_ => eq_expr_value(cx, local_expr, expr),
}
}
/// Checks if an expression is a path to a local variable (with optional projections), e.g.
/// `x.field[0].field2` would return true.
fn is_local_with_projections(expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Path(_) => path_to_local(expr).is_some(),
ExprKind::Field(expr, _) | ExprKind::Index(expr, ..) => is_local_with_projections(expr),
_ => false,
}
}
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
@ -36,9 +82,8 @@ pub(super) fn check<'tcx>(
&& let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind
&& cx.typeck_results().expr_ty(receiver).is_bool()
&& path.ident.name == sym!(then_some)
&& let ExprKind::Binary(_, lhs, rhs) = receiver.kind
&& let Some(local_id) = path_to_local(transmutable)
&& (path_to_local_id(lhs, local_id) || path_to_local_id(rhs, local_id))
&& 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

View file

@ -37,9 +37,7 @@ pub(super) fn check<'tcx>(
to_ty = to_sub_ty;
continue;
},
(ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty)))
if reduced_tys.from_fat_ptr =>
{
(ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => {
from_ty = from_sub_ty;
to_ty = to_sub_ty;
continue;

View file

@ -1,14 +1,21 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{get_trait_def_id, path_res};
use clippy_utils::{expr_or_init, get_trait_def_id, path_def_id};
use rustc_ast::BinOpKind;
use rustc_hir::def::Res;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Expr, ExprKind, FnDecl, Item, ItemKind, Node};
use rustc_hir::intravisit::{walk_body, walk_expr, FnKind, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::declare_lint_pass;
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt};
use rustc_session::impl_lint_pass;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{sym, Span};
use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
declare_clippy_lint! {
/// ### What it does
@ -41,7 +48,26 @@ declare_clippy_lint! {
"detect unconditional recursion in some traits implementation"
}
declare_lint_pass!(UnconditionalRecursion => [UNCONDITIONAL_RECURSION]);
#[derive(Default)]
pub struct UnconditionalRecursion {
/// The key is the `DefId` of the type implementing the `Default` trait and the value is the
/// `DefId` of the return call.
default_impl_for_type: FxHashMap<DefId, DefId>,
}
impl_lint_pass!(UnconditionalRecursion => [UNCONDITIONAL_RECURSION]);
fn span_error(cx: &LateContext<'_>, method_span: Span, expr: &Expr<'_>) {
span_lint_and_then(
cx,
UNCONDITIONAL_RECURSION,
method_span,
"function cannot return without recursing",
|diag| {
diag.span_note(expr.span, "recursive call site");
},
);
}
fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
match ty.peel_refs().kind() {
@ -51,84 +77,329 @@ fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
}
}
fn is_local(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
matches!(path_res(cx, expr), Res::Local(_))
fn get_hir_ty_def_id(tcx: TyCtxt<'_>, hir_ty: rustc_hir::Ty<'_>) -> Option<DefId> {
let TyKind::Path(qpath) = hir_ty.kind else { return None };
match qpath {
QPath::Resolved(_, path) => path.res.opt_def_id(),
QPath::TypeRelative(_, _) => {
let ty = hir_ty_to_ty(tcx, &hir_ty);
match ty.kind() {
ty::Alias(ty::Projection, proj) => {
Res::<HirId>::Def(DefKind::Trait, proj.trait_ref(tcx).def_id).opt_def_id()
},
_ => None,
}
},
QPath::LangItem(..) => None,
}
}
fn get_return_calls_in_body<'tcx>(body: &'tcx Body<'tcx>) -> Vec<&'tcx Expr<'tcx>> {
let mut visitor = ReturnsVisitor::default();
visitor.visit_body(body);
visitor.returns
}
fn has_conditional_return(body: &Body<'_>, expr: &Expr<'_>) -> bool {
match get_return_calls_in_body(body).as_slice() {
[] => false,
[return_expr] => return_expr.hir_id != expr.hir_id,
_ => true,
}
}
fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Option<DefId> {
let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id);
if let Some((
_,
Node::Item(Item {
kind: ItemKind::Impl(impl_),
owner_id,
..
}),
)) = cx.tcx.hir().parent_iter(hir_id).next()
// We exclude `impl` blocks generated from rustc's proc macros.
&& !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
// It is a implementation of a trait.
&& let Some(trait_) = impl_.of_trait
{
trait_.trait_def_id()
} else {
None
}
}
#[allow(clippy::unnecessary_def_path)]
fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) {
let args = cx
.tcx
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder())
.inputs();
// That has two arguments.
if let [self_arg, other_arg] = args
&& let Some(self_arg) = get_ty_def_id(*self_arg)
&& let Some(other_arg) = get_ty_def_id(*other_arg)
// The two arguments are of the same type.
&& self_arg == other_arg
&& let Some(trait_def_id) = get_impl_trait_def_id(cx, method_def_id)
// The trait is `PartialEq`.
&& Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"])
{
let to_check_op = if name.name == sym::eq {
BinOpKind::Eq
} else {
BinOpKind::Ne
};
let is_bad = match expr.kind {
ExprKind::Binary(op, left, right) if op.node == to_check_op => {
// Then we check if the left-hand element is of the same type as `self`.
if let Some(left_ty) = cx.typeck_results().expr_ty_opt(left)
&& let Some(left_id) = get_ty_def_id(left_ty)
&& self_arg == left_id
&& let Some(right_ty) = cx.typeck_results().expr_ty_opt(right)
&& let Some(right_id) = get_ty_def_id(right_ty)
&& other_arg == right_id
{
true
} else {
false
}
},
ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => {
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
&& trait_id == trait_def_id
{
true
} else {
false
}
},
_ => false,
};
if is_bad {
span_error(cx, method_span, expr);
}
}
}
#[allow(clippy::unnecessary_def_path)]
fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) {
let args = cx
.tcx
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder())
.inputs();
// That has one argument.
if let [_self_arg] = args
&& let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id)
&& let Some((
_,
Node::Item(Item {
kind: ItemKind::Impl(impl_),
owner_id,
..
}),
)) = cx.tcx.hir().parent_iter(hir_id).next()
// We exclude `impl` blocks generated from rustc's proc macros.
&& !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
// It is a implementation of a trait.
&& let Some(trait_) = impl_.of_trait
&& let Some(trait_def_id) = trait_.trait_def_id()
// The trait is `ToString`.
&& Some(trait_def_id) == get_trait_def_id(cx, &["alloc", "string", "ToString"])
{
let is_bad = match expr.kind {
ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => {
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
&& trait_id == trait_def_id
{
true
} else {
false
}
},
_ => false,
};
if is_bad {
span_error(cx, method_span, expr);
}
}
}
fn is_default_method_on_current_ty(tcx: TyCtxt<'_>, qpath: QPath<'_>, implemented_ty_id: DefId) -> bool {
match qpath {
QPath::Resolved(_, path) => match path.segments {
[first, .., last] => last.ident.name == kw::Default && first.res.opt_def_id() == Some(implemented_ty_id),
_ => false,
},
QPath::TypeRelative(ty, segment) => {
if segment.ident.name != kw::Default {
return false;
}
if matches!(
ty.kind,
TyKind::Path(QPath::Resolved(
_,
hir::Path {
res: Res::SelfTyAlias { .. },
..
},
))
) {
return true;
}
get_hir_ty_def_id(tcx, *ty) == Some(implemented_ty_id)
},
QPath::LangItem(..) => false,
}
}
struct CheckCalls<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
map: Map<'tcx>,
implemented_ty_id: DefId,
found_default_call: bool,
method_span: Span,
}
impl<'a, 'tcx> Visitor<'tcx> for CheckCalls<'a, 'tcx>
where
'tcx: 'a,
{
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.map
}
#[allow(clippy::unnecessary_def_path)]
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if self.found_default_call {
return;
}
walk_expr(self, expr);
if let ExprKind::Call(f, _) = expr.kind
&& let ExprKind::Path(qpath) = f.kind
&& is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id)
&& let Some(method_def_id) = path_def_id(self.cx, f)
&& let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id)
&& Some(trait_def_id) == get_trait_def_id(self.cx, &["core", "default", "Default"])
{
self.found_default_call = true;
span_error(self.cx, self.method_span, expr);
}
}
}
impl UnconditionalRecursion {
#[allow(clippy::unnecessary_def_path)]
fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) {
if self.default_impl_for_type.is_empty()
&& let Some(default_trait_id) = get_trait_def_id(cx, &["core", "default", "Default"])
{
let impls = cx.tcx.trait_impls_of(default_trait_id);
for (ty, impl_def_ids) in impls.non_blanket_impls() {
let Some(self_def_id) = ty.def() else { continue };
for impl_def_id in impl_def_ids {
if !cx.tcx.has_attr(*impl_def_id, sym::automatically_derived) &&
let Some(assoc_item) = cx
.tcx
.associated_items(impl_def_id)
.in_definition_order()
// We're not interested in foreign implementations of the `Default` trait.
.find(|item| {
item.kind == AssocKind::Fn && item.def_id.is_local() && item.name == kw::Default
})
&& let Some(body_node) = cx.tcx.hir().get_if_local(assoc_item.def_id)
&& let Some(body_id) = body_node.body_id()
&& let body = cx.tcx.hir().body(body_id)
// We don't want to keep it if it has conditional return.
&& let [return_expr] = get_return_calls_in_body(body).as_slice()
&& let ExprKind::Call(call_expr, _) = return_expr.kind
// We need to use typeck here to infer the actual function being called.
&& let body_def_id = cx.tcx.hir().enclosing_body_owner(call_expr.hir_id)
&& let Some(body_owner) = cx.tcx.hir().maybe_body_owned_by(body_def_id)
&& let typeck = cx.tcx.typeck_body(body_owner)
&& let Some(call_def_id) = typeck.type_dependent_def_id(call_expr.hir_id)
{
self.default_impl_for_type.insert(self_def_id, call_def_id);
}
}
}
}
}
fn check_default_new<'tcx>(
&mut self,
cx: &LateContext<'tcx>,
decl: &FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
method_span: Span,
method_def_id: LocalDefId,
) {
// We're only interested into static methods.
if decl.implicit_self.has_implicit_self() {
return;
}
// We don't check trait implementations.
if get_impl_trait_def_id(cx, method_def_id).is_some() {
return;
}
let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id);
if let Some((
_,
Node::Item(Item {
kind: ItemKind::Impl(impl_),
..
}),
)) = cx.tcx.hir().parent_iter(hir_id).next()
&& let Some(implemented_ty_id) = get_hir_ty_def_id(cx.tcx, *impl_.self_ty)
&& {
self.init_default_impl_for_type_if_needed(cx);
true
}
&& let Some(return_def_id) = self.default_impl_for_type.get(&implemented_ty_id)
&& method_def_id.to_def_id() == *return_def_id
{
let mut c = CheckCalls {
cx,
map: cx.tcx.hir(),
implemented_ty_id,
found_default_call: false,
method_span,
};
walk_body(&mut c, body);
}
}
}
impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
#[allow(clippy::unnecessary_def_path)]
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
kind: FnKind<'tcx>,
_decl: &'tcx FnDecl<'tcx>,
decl: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
method_span: Span,
def_id: LocalDefId,
method_def_id: LocalDefId,
) {
// If the function is a method...
if let FnKind::Method(name, _) = kind
// That has two arguments.
&& let [self_arg, other_arg] = cx
.tcx
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder())
.inputs()
&& let Some(self_arg) = get_ty_def_id(*self_arg)
&& let Some(other_arg) = get_ty_def_id(*other_arg)
// The two arguments are of the same type.
&& self_arg == other_arg
&& let hir_id = cx.tcx.local_def_id_to_hir_id(def_id)
&& let Some((
_,
Node::Item(Item {
kind: ItemKind::Impl(impl_),
owner_id,
..
}),
)) = cx.tcx.hir().parent_iter(hir_id).next()
// We exclude `impl` blocks generated from rustc's proc macros.
&& !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
// It is a implementation of a trait.
&& let Some(trait_) = impl_.of_trait
&& let Some(trait_def_id) = trait_.trait_def_id()
// The trait is `PartialEq`.
&& Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"])
&& let expr = expr_or_init(cx, body.value).peel_blocks()
// Doesn't have a conditional return.
&& !has_conditional_return(body, expr)
{
let to_check_op = if name.name == sym::eq {
BinOpKind::Eq
} else {
BinOpKind::Ne
};
let expr = body.value.peel_blocks();
let is_bad = match expr.kind {
ExprKind::Binary(op, left, right) if op.node == to_check_op => {
is_local(cx, left) && is_local(cx, right)
},
ExprKind::MethodCall(segment, receiver, &[arg], _) if segment.ident.name == name.name => {
if is_local(cx, receiver)
&& is_local(cx, &arg)
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
&& trait_id == trait_def_id
{
true
} else {
false
}
},
_ => false,
};
if is_bad {
span_lint_and_then(
cx,
UNCONDITIONAL_RECURSION,
method_span,
"function cannot return without recursing",
|diag| {
diag.span_note(expr.span, "recursive call site");
},
);
if name.name == sym::eq || name.name == sym::ne {
check_partial_eq(cx, method_span, method_def_id, name, expr);
} else if name.name == sym::to_string {
check_to_string(cx, method_span, method_def_id, name, expr);
}
self.check_default_new(cx, decl, body, method_span, method_def_id);
}
}
}

View file

@ -681,11 +681,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos
.filter(|(_, text)| !text.is_empty());
let (line_start, line) = lines.next()?;
let mut in_codeblock = false;
// Check for a sequence of line comments.
if line.starts_with("//") {
let (mut line, mut line_start) = (line, line_start);
loop {
if line.to_ascii_uppercase().contains("SAFETY:") {
// Don't lint if the safety comment is part of a codeblock in a doc comment.
// It may or may not be required, and we can't very easily check it (and we shouldn't, since
// the safety comment isn't referring to the node we're currently checking)
if line.trim_start_matches("///").trim_start().starts_with("```") {
in_codeblock = !in_codeblock;
}
if line.to_ascii_uppercase().contains("SAFETY:") && !in_codeblock {
return Some(start_pos + BytePos(u32::try_from(line_start).unwrap()));
}
match lines.next() {

View file

@ -13,12 +13,31 @@ use rustc_middle::ty;
use super::LET_UNIT_VALUE;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
// skip `let () = { ... }`
if let PatKind::Tuple(fields, ..) = local.pat.kind
&& fields.is_empty()
{
return;
}
if let Some(init) = local.init
&& !local.pat.span.from_expansion()
&& !in_external_macro(cx.sess(), local.span)
&& !is_from_async_await(local.span)
&& cx.typeck_results().pat_ty(local.pat).is_unit()
{
// skip `let awa = ()`
if let ExprKind::Tup([]) = init.kind {
return;
}
// skip `let _: () = { ... }`
if let Some(ty) = local.ty
&& let TyKind::Tup([]) = ty.kind
{
return;
}
if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
|| matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()))
&& expr_needs_inferred_result(cx, init)
@ -34,7 +53,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|diag| {
diag.span_suggestion(
local.pat.span,
"use a wild (`_`) binding",
"use a wildcard binding",
"_",
Applicability::MaybeIncorrect, // snippet
);

View file

@ -11,8 +11,8 @@ use clippy_utils::{get_parent_expr, higher, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
use rustc_span::{sym, DesugaringKind, Span};
@ -79,7 +79,6 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
// this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
&& local.ty.is_none()
&& let PatKind::Binding(_, id, ..) = local.pat.kind
&& is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr.peel_borrows())))
{
let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
// allow indexing into a vec and some set of allowed method calls that exist on slices, too
@ -185,6 +184,11 @@ impl UselessVec {
let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => {
if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
// vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also
if !is_copy(cx, cx.typeck_results().expr_ty(elem)) {
return;
}
#[expect(clippy::cast_possible_truncation)]
if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
return;
@ -241,12 +245,3 @@ fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
let ty = cx.typeck_results().expr_ty_adjusted(expr);
cx.layout_of(ty).map_or(0, |l| l.size.bytes())
}
/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
fn vec_type(ty: Ty<'_>) -> Ty<'_> {
if let ty::Adt(_, args) = ty.kind() {
args.type_at(0)
} else {
panic!("The type of `vec!` is a not a struct?");
}
}

View file

@ -142,7 +142,7 @@ impl LateLintPass<'_> for WildcardImports {
} else {
// In this case, the `use_path.span` ends right before the `::*`, so we need to
// extend it up to the `*`. Since it is hard to find the `*` in weird
// formattings like `use _ :: *;`, we extend it up to, but not including the
// formatting like `use _ :: *;`, we extend it up to, but not including the
// `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
// can just use the end of the item span
let mut span = use_path.span.with_hi(item.span.hi());

View file

@ -99,7 +99,10 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
}
fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
if let Res::Def(DefKind::Ctor(..) | DefKind::Variant | DefKind::Enum | DefKind::Struct, _)
| Res::SelfCtor(_)
| Res::SelfTyAlias { .. } = res
{
cx.typeck_results()
.expr_ty(e)
.has_significant_drop(cx.tcx, cx.param_env)
@ -173,6 +176,13 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
self.eagerness |= NoChange;
return;
},
#[expect(clippy::match_same_arms)] // arm pattern can't be merged due to `ref`, see rust#105778
ExprKind::Struct(path, ..) => {
if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
self.eagerness = ForceNoChange;
return;
}
},
ExprKind::Path(ref path) => {
if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
self.eagerness = ForceNoChange;
@ -291,7 +301,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
| ExprKind::Closure { .. }
| ExprKind::Field(..)
| ExprKind::AddrOf(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
| ExprKind::Block(Block { stmts: [], .. }, _)
| ExprKind::OffsetOf(..) => (),

View file

@ -865,7 +865,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
for arm in arms {
self.hash_pat(arm.pat);
if let Some(ref e) = arm.guard {
if let Some(e) = arm.guard {
self.hash_expr(e);
}
self.hash_expr(arm.body);

View file

@ -420,7 +420,7 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId)
ast_format_args
.get()?
.get(&format_args_expr.span.with_parent(None))
.map(Rc::clone)
.cloned()
})
}

View file

@ -38,7 +38,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
ExprKind::Call(callee, args) => {
let lhs = expr_type_certainty(cx, callee);
let rhs = if type_is_inferrable_from_arguments(cx, expr) {
let rhs = if type_is_inferable_from_arguments(cx, expr) {
meet(args.iter().map(|arg| expr_type_certainty(cx, arg)))
} else {
Certainty::Uncertain
@ -57,7 +57,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id);
};
let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false);
let rhs = if type_is_inferrable_from_arguments(cx, expr) {
let rhs = if type_is_inferable_from_arguments(cx, expr) {
meet(
std::iter::once(receiver_type_certainty).chain(args.iter().map(|arg| expr_type_certainty(cx, arg))),
)
@ -279,7 +279,7 @@ fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &
}
#[allow(clippy::cast_possible_truncation)]
fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let Some(callee_def_id) = (match expr.kind {
ExprKind::Call(callee, _) => {
let callee_ty = cx.typeck_results().expr_ty(callee);

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2023-12-28"
channel = "nightly-2024-01-11"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View file

@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under:
<!-- REUSE-IgnoreStart -->
Copyright 2014-2022 The Rust Project Developers
Copyright 2014-2024 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license

View file

@ -0,0 +1 @@
pub-underscore-fields-behavior = "AllPubFields"

View file

@ -0,0 +1 @@
pub-underscore-fields-behavior = "PublicallyExported"

View file

@ -0,0 +1,60 @@
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:15:9
|
LL | pub _b: u8,
| ^^^^^^
|
= help: consider removing the underscore, or making the field private
= note: `-D clippy::pub-underscore-fields` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]`
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:23:13
|
LL | pub(in crate::inner) _f: Option<()>,
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider removing the underscore, or making the field private
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:27:13
|
LL | pub _g: String,
| ^^^^^^
|
= help: consider removing the underscore, or making the field private
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:34:9
|
LL | pub _a: usize,
| ^^^^^^
|
= help: consider removing the underscore, or making the field private
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:41:9
|
LL | pub _c: i64,
| ^^^^^^
|
= help: consider removing the underscore, or making the field private
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:44:9
|
LL | pub _e: Option<u8>,
| ^^^^^^
|
= help: consider removing the underscore, or making the field private
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:57:9
|
LL | pub(crate) _b: Option<String>,
| ^^^^^^^^^^^^^
|
= help: consider removing the underscore, or making the field private
error: aborting due to 7 previous errors

View file

@ -0,0 +1,12 @@
error: field marked as public but also inferred as unused because it's prefixed with `_`
--> $DIR/pub_underscore_fields.rs:15:9
|
LL | pub _b: u8,
| ^^^^^^
|
= help: consider removing the underscore, or making the field private
= note: `-D clippy::pub-underscore-fields` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]`
error: aborting due to 1 previous error

View file

@ -0,0 +1,66 @@
//@revisions: exported all_pub_fields
//@[all_pub_fields] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/pub_underscore_fields/all_pub_fields
//@[exported] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/pub_underscore_fields/exported
#![allow(unused)]
#![warn(clippy::pub_underscore_fields)]
use std::marker::PhantomData;
pub mod inner {
use std::marker;
pub struct PubSuper {
pub(super) a: usize,
pub _b: u8,
_c: i32,
pub _mark: marker::PhantomData<u8>,
}
mod inner2 {
pub struct PubModVisibility {
pub(in crate::inner) e: bool,
pub(in crate::inner) _f: Option<()>,
}
struct PrivateStructPubField {
pub _g: String,
}
}
}
fn main() {
pub struct StructWithOneViolation {
pub _a: usize,
}
// should handle structs with multiple violations
pub struct StructWithMultipleViolations {
a: u8,
_b: usize,
pub _c: i64,
#[doc(hidden)]
pub d: String,
pub _e: Option<u8>,
}
// shouldn't warn on anonymous fields
pub struct AnonymousFields(pub usize, i32);
// don't warn on empty structs
pub struct Empty1;
pub struct Empty2();
pub struct Empty3 {};
pub struct PubCrate {
pub(crate) a: String,
pub(crate) _b: Option<String>,
}
// shouldn't warn on fields named pub
pub struct NamedPub {
r#pub: bool,
_pub: String,
pub(crate) _mark: PhantomData<u8>,
}
}

View file

@ -49,6 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
missing-docs-in-crate-items
msrv
pass-by-value-size-limit
pub-underscore-fields-behavior
semicolon-inside-block-ignore-singleline
semicolon-outside-block-ignore-multiline
single-char-binding-names-threshold
@ -124,6 +125,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
missing-docs-in-crate-items
msrv
pass-by-value-size-limit
pub-underscore-fields-behavior
semicolon-inside-block-ignore-singleline
semicolon-outside-block-ignore-multiline
single-char-binding-names-threshold

View file

@ -17,7 +17,7 @@ fn main() {
with_span!(
span
fn coverting() {
fn converting() {
let x = 0u32 as u64;
}
);

View file

@ -365,3 +365,52 @@ fn avoid_subtract_overflow(q: u32) {
fn issue11426() {
(&42u8 >> 0xa9008fb6c9d81e42_0e25730562a601c8_u128) as usize;
}
fn issue11642() {
fn square(x: i16) -> u32 {
let x = x as i32;
(x * x) as u32;
x.pow(2) as u32;
(-2_i32).pow(2) as u32
}
let _a = |x: i32| -> u32 { (x * x * x * x) as u32 };
(-2_i32).pow(3) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
let x: i32 = 10;
(x * x) as u32;
(x * x * x) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
let y: i16 = -2;
(y * y * y * y * -2) as u16;
//~^ ERROR: casting `i16` to `u16` may lose the sign of the value
(y * y * y * y * 2) as u16;
(y * y * y * 2) as u16;
//~^ ERROR: casting `i16` to `u16` may lose the sign of the value
(y * y * y * -2) as u16;
//~^ ERROR: casting `i16` to `u16` may lose the sign of the value
fn foo(a: i32, b: i32, c: i32) -> u32 {
(a * a * b * b * c * c) as u32;
(a * b * c) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
(a * -b * c) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
(a * b * c * c) as u32;
(a * -2) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
(a * b * c * -2) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
(a / b) as u32;
(a / b * c) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
(a / b + b * c) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
a.pow(3) as u32;
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
(a.abs() * b.pow(2) / c.abs()) as u32
}
}

View file

@ -444,5 +444,77 @@ help: ... or use `try_from` and handle the error accordingly
LL | let c = u8::try_from(q / 1000);
| ~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 51 previous errors
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:379:5
|
LL | (-2_i32).pow(3) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:384:5
|
LL | (x * x * x) as u32;
| ^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value
--> $DIR/cast.rs:388:5
|
LL | (y * y * y * y * -2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value
--> $DIR/cast.rs:391:5
|
LL | (y * y * y * 2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value
--> $DIR/cast.rs:393:5
|
LL | (y * y * y * -2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:398:9
|
LL | (a * b * c) as u32;
| ^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:400:9
|
LL | (a * -b * c) as u32;
| ^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:403:9
|
LL | (a * -2) as u32;
| ^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:405:9
|
LL | (a * b * c * -2) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:408:9
|
LL | (a / b * c) as u32;
| ^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:410:9
|
LL | (a / b + b * c) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:412:9
|
LL | a.pow(3) as u32;
| ^^^^^^^^^^^^^^^
error: aborting due to 63 previous errors

View file

@ -1,10 +0,0 @@
#![warn(clippy::let_unit_value)]
fn f() {}
static FN: fn() = f;
fn main() {
FN();
//~^ ERROR: this let-binding has unit value
//~| NOTE: `-D clippy::let-unit-value` implied by `-D warnings`
}

View file

@ -5,6 +5,4 @@ static FN: fn() = f;
fn main() {
let _: () = FN();
//~^ ERROR: this let-binding has unit value
//~| NOTE: `-D clippy::let-unit-value` implied by `-D warnings`
}

View file

@ -1,11 +0,0 @@
error: this let-binding has unit value
--> $DIR/ice-8821.rs:7:5
|
LL | let _: () = FN();
| ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();`
|
= note: `-D clippy::let-unit-value` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]`
error: aborting due to 1 previous error

View file

@ -73,9 +73,7 @@ mod nested_local {
mod function_def {
fn ret_f64() -> f64 {
// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
1.0_f64
1.
}
fn test() {

View file

@ -73,8 +73,6 @@ mod nested_local {
mod function_def {
fn ret_f64() -> f64 {
// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
1.
}

View file

@ -86,66 +86,60 @@ LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:78:9
|
LL | 1.
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:84:27
--> $DIR/default_numeric_fallback_f64.rs:82:27
|
LL | let f = || -> _ { 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:88:29
--> $DIR/default_numeric_fallback_f64.rs:86:29
|
LL | let f = || -> f64 { 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:102:21
--> $DIR/default_numeric_fallback_f64.rs:100:21
|
LL | generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:105:32
--> $DIR/default_numeric_fallback_f64.rs:103:32
|
LL | let x: _ = generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:123:28
--> $DIR/default_numeric_fallback_f64.rs:121:28
|
LL | GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:126:36
--> $DIR/default_numeric_fallback_f64.rs:124:36
|
LL | let _ = GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:144:24
--> $DIR/default_numeric_fallback_f64.rs:142:24
|
LL | GenericEnum::X(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:164:23
--> $DIR/default_numeric_fallback_f64.rs:162:23
|
LL | s.generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:174:25
--> $DIR/default_numeric_fallback_f64.rs:172:25
|
LL | inline!(let x = 22.;);
| ^^^ help: consider adding suffix: `22.0_f64`
|
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 24 previous errors
error: aborting due to 23 previous errors

View file

@ -74,9 +74,7 @@ mod nested_local {
mod function_def {
fn ret_i32() -> i32 {
// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
1_i32
1
}
fn test() {
@ -186,4 +184,36 @@ fn check_expect_suppression() {
let x = 21;
}
mod type_already_inferred {
// Should NOT lint if bound to return type
fn ret_i32() -> i32 {
1
}
// Should NOT lint if bound to return type
fn ret_if_i32(b: bool) -> i32 {
if b { 100 } else { 0 }
}
// Should NOT lint if bound to return type
fn ret_i32_tuple() -> (i32, i32) {
(0, 1)
}
// Should NOT lint if bound to return type
fn ret_stmt(b: bool) -> (i32, i32) {
if b {
return (0, 1);
}
(0, 0)
}
#[allow(clippy::useless_vec)]
fn vec_macro() {
// Should NOT lint in `vec!` call if the type was already stated
let data_i32: Vec<i32> = vec![1, 2, 3];
let data_i32 = vec![1_i32, 2_i32, 3_i32];
}
}
fn main() {}

View file

@ -74,8 +74,6 @@ mod nested_local {
mod function_def {
fn ret_i32() -> i32 {
// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
1
}
@ -186,4 +184,36 @@ fn check_expect_suppression() {
let x = 21;
}
mod type_already_inferred {
// Should NOT lint if bound to return type
fn ret_i32() -> i32 {
1
}
// Should NOT lint if bound to return type
fn ret_if_i32(b: bool) -> i32 {
if b { 100 } else { 0 }
}
// Should NOT lint if bound to return type
fn ret_i32_tuple() -> (i32, i32) {
(0, 1)
}
// Should NOT lint if bound to return type
fn ret_stmt(b: bool) -> (i32, i32) {
if b {
return (0, 1);
}
(0, 0)
}
#[allow(clippy::useless_vec)]
fn vec_macro() {
// Should NOT lint in `vec!` call if the type was already stated
let data_i32: Vec<i32> = vec![1, 2, 3];
let data_i32 = vec![1, 2, 3];
}
}
fn main() {}

View file

@ -98,66 +98,78 @@ LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:79:9
|
LL | 1
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:85:27
--> $DIR/default_numeric_fallback_i32.rs:83:27
|
LL | let f = || -> _ { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:89:29
--> $DIR/default_numeric_fallback_i32.rs:87:29
|
LL | let f = || -> i32 { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:103:21
--> $DIR/default_numeric_fallback_i32.rs:101:21
|
LL | generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:106:32
--> $DIR/default_numeric_fallback_i32.rs:104:32
|
LL | let x: _ = generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:124:28
--> $DIR/default_numeric_fallback_i32.rs:122:28
|
LL | GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:127:36
--> $DIR/default_numeric_fallback_i32.rs:125:36
|
LL | let _ = GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:145:24
--> $DIR/default_numeric_fallback_i32.rs:143:24
|
LL | GenericEnum::X(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:165:23
--> $DIR/default_numeric_fallback_i32.rs:163:23
|
LL | s.generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:175:25
--> $DIR/default_numeric_fallback_i32.rs:173:25
|
LL | inline!(let x = 22;);
| ^^ help: consider adding suffix: `22_i32`
|
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 26 previous errors
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:215:29
|
LL | let data_i32 = vec![1, 2, 3];
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:215:32
|
LL | let data_i32 = vec![1, 2, 3];
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:215:35
|
LL | let data_i32 = vec![1, 2, 3];
| ^ help: consider adding suffix: `3_i32`
error: aborting due to 28 previous errors

View file

@ -12,16 +12,49 @@ enum Opcode {
Div = 3,
}
struct Data {
foo: &'static [u8],
bar: &'static [u8],
}
fn int_to_opcode(op: u8) -> Option<Opcode> {
(op < 4).then(|| unsafe { std::mem::transmute(op) })
}
fn f(op: u8, unrelated: u8) {
fn f(op: u8, op2: Data, unrelated: u8) {
true.then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
(unrelated < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
(op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
(op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
(op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
let _: Option<Opcode> = (op > 0 && op < 10).then(|| unsafe { std::mem::transmute(op) });
let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then(|| unsafe { std::mem::transmute(op) });
// lint even when the transmutable goes through field/array accesses
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| unsafe { std::mem::transmute(op2.foo[0]) });
// don't lint: wrong index used in the transmute
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[1]) });
// don't lint: no check for the transmutable in the condition
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op2.bar[0]) });
// don't lint: wrong variable
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op) });
// range contains checks
let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) });
let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) });
let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
// unrelated binding in contains
let _: Option<Opcode> = (1..=3)
.contains(&unrelated)
.then_some(unsafe { std::mem::transmute(op) });
}
unsafe fn f2(op: u8) {

Some files were not shown because too many files have changed in this diff Show more