diff --git a/Cargo.lock b/Cargo.lock index ef9f91fdb434b..aa3d704108fb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,7 +613,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.59" +version = "0.1.60" dependencies = [ "cargo_metadata 0.14.0", "clippy_lints", @@ -621,6 +621,7 @@ dependencies = [ "compiletest_rs", "derive-new", "filetime", + "futures 0.3.12", "if_chain", "itertools 0.10.1", "parking_lot", @@ -633,6 +634,7 @@ dependencies = [ "syn", "tempfile", "tester", + "tokio", ] [[package]] @@ -652,7 +654,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.59" +version = "0.1.60" dependencies = [ "cargo_metadata 0.14.0", "clippy_utils", @@ -673,8 +675,9 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" dependencies = [ + "arrayvec", "if_chain", "rustc-semver", ] diff --git a/RELEASES.md b/RELEASES.md index 01c57ab917033..0b44fbab24ce5 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -100,19 +100,14 @@ and related tools. [87467]: https://github.com/rust-lang/rust/pull/87467/ [87704]: https://github.com/rust-lang/rust/pull/87704/ [88041]: https://github.com/rust-lang/rust/pull/88041/ -[88300]: https://github.com/rust-lang/rust/pull/88300/ [88447]: https://github.com/rust-lang/rust/pull/88447/ [88601]: https://github.com/rust-lang/rust/pull/88601/ -[88624]: https://github.com/rust-lang/rust/pull/88624/ [89062]: https://github.com/rust-lang/rust/pull/89062/ [89174]: https://github.com/rust-lang/rust/pull/89174/ -[89542]: https://github.com/rust-lang/rust/pull/89542/ [89551]: https://github.com/rust-lang/rust/pull/89551/ [89558]: https://github.com/rust-lang/rust/pull/89558/ [89580]: https://github.com/rust-lang/rust/pull/89580/ [89652]: https://github.com/rust-lang/rust/pull/89652/ -[89677]: https://github.com/rust-lang/rust/pull/89677/ -[89951]: https://github.com/rust-lang/rust/pull/89951/ [90041]: https://github.com/rust-lang/rust/pull/90041/ [90058]: https://github.com/rust-lang/rust/pull/90058/ [90104]: https://github.com/rust-lang/rust/pull/90104/ @@ -128,11 +123,9 @@ and related tools. [90733]: https://github.com/rust-lang/rust/pull/90733/ [90833]: https://github.com/rust-lang/rust/pull/90833/ [90846]: https://github.com/rust-lang/rust/pull/90846/ -[90896]: https://github.com/rust-lang/rust/pull/90896/ [91026]: https://github.com/rust-lang/rust/pull/91026/ [91207]: https://github.com/rust-lang/rust/pull/91207/ [91255]: https://github.com/rust-lang/rust/pull/91255/ -[91301]: https://github.com/rust-lang/rust/pull/91301/ [cargo/10082]: https://github.com/rust-lang/cargo/pull/10082/ [cargo/10107]: https://github.com/rust-lang/cargo/pull/10107/ [`Metadata::is_symlink`]: https://doc.rust-lang.org/stable/std/fs/struct.Metadata.html#method.is_symlink @@ -143,28 +136,7 @@ and related tools. [`Result::unwrap_err_unchecked`]: https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.unwrap_err_unchecked [`NonZero{unsigned}::is_power_of_two`]: https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html#method.is_power_of_two [`File::options`]: https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.options -[`unix::process::ExitStatusExt::core_dumped`]: https://doc.rust-lang.org/stable/std/os/unix/process/trait.ExitStatusExt.html#tymethod.core_dumped -[`unix::process::ExitStatusExt::stopped_signal`]: https://doc.rust-lang.org/stable/std/os/unix/process/trait.ExitStatusExt.html#tymethod.stopped_signal -[`unix::process::ExitStatusExt::continued`]: https://doc.rust-lang.org/stable/std/os/unix/process/trait.ExitStatusExt.html#tymethod.continued -[`unix::process::ExitStatusExt::into_raw`]: https://doc.rust-lang.org/stable/std/os/unix/process/trait.ExitStatusExt.html#tymethod.into_raw [`Duration::new`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.new -[`Duration::checked_add`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.checked_add -[`Duration::saturating_add`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.saturating_add -[`Duration::checked_sub`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.checked_sub -[`Duration::saturating_sub`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.saturating_sub -[`Duration::checked_mul`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.checked_mul -[`Duration::saturating_mul`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.saturating_mul -[`Duration::checked_div`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.checked_div -[`Duration::as_secs_f64`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.as_secs_f64 -[`Duration::as_secs_f32`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.as_secs_f32 -[`Duration::from_secs_f64`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_secs_f64 -[`Duration::from_secs_f32`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_secs_f32 -[`Duration::mul_f64`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.mul_f64 -[`Duration::mul_f32`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.mul_f32 -[`Duration::div_f64`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.div_f64 -[`Duration::div_f32`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.div_f32 -[`Duration::div_duration_f64`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.div_duration_f64 -[`Duration::div_duration_f32`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.div_duration_f32 [`MaybeUninit::as_ptr`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.as_ptr [`MaybeUninit::as_mut_ptr`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.as_mut_ptr [`MaybeUninit::assume_init`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init @@ -250,7 +222,6 @@ and related tools. [86191]: https://github.com/rust-lang/rust/pull/86191/ [87220]: https://github.com/rust-lang/rust/pull/87220/ [87260]: https://github.com/rust-lang/rust/pull/87260/ -[88243]: https://github.com/rust-lang/rust/pull/88243/ [88321]: https://github.com/rust-lang/rust/pull/88321/ [88529]: https://github.com/rust-lang/rust/pull/88529/ [88690]: https://github.com/rust-lang/rust/pull/88690/ @@ -406,8 +377,6 @@ and related tools. as well as rustdoc. [`std::os::unix::fs::chroot`]: https://doc.rust-lang.org/stable/std/os/unix/fs/fn.chroot.html -[`Iterator::intersperse`]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.intersperse -[`Iterator::intersperse_with`]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.intersperse [`UnsafeCell::raw_get`]: https://doc.rust-lang.org/stable/std/cell/struct.UnsafeCell.html#method.raw_get [`BufWriter::into_parts`]: https://doc.rust-lang.org/stable/std/io/struct.BufWriter.html#method.into_parts [`core::panic::{UnwindSafe, RefUnwindSafe, AssertUnwindSafe}`]: https://github.com/rust-lang/rust/pull/84662 @@ -429,12 +398,7 @@ and related tools. [rust#86183]: https://github.com/rust-lang/rust/pull/86183 [rust#87385]: https://github.com/rust-lang/rust/pull/87385 [rust#88100]: https://github.com/rust-lang/rust/pull/88100 -[rust#86860]: https://github.com/rust-lang/rust/pull/86860 -[rust#84039]: https://github.com/rust-lang/rust/pull/84039 -[rust#86492]: https://github.com/rust-lang/rust/pull/86492 -[rust#88363]: https://github.com/rust-lang/rust/pull/88363 [rust#85305]: https://github.com/rust-lang/rust/pull/85305 -[rust#87832]: https://github.com/rust-lang/rust/pull/87832 [rust#88069]: https://github.com/rust-lang/rust/pull/88069 [rust#87472]: https://github.com/rust-lang/rust/pull/87472 [rust#87699]: https://github.com/rust-lang/rust/pull/87699 @@ -445,31 +409,12 @@ and related tools. [rust#87580]: https://github.com/rust-lang/rust/pull/87580 [rust#83342]: https://github.com/rust-lang/rust/pull/83342 [rust#83093]: https://github.com/rust-lang/rust/pull/83093 -[rust#88177]: https://github.com/rust-lang/rust/pull/88177 -[rust#88548]: https://github.com/rust-lang/rust/pull/88548 -[rust#88551]: https://github.com/rust-lang/rust/pull/88551 -[rust#88299]: https://github.com/rust-lang/rust/pull/88299 -[rust#88220]: https://github.com/rust-lang/rust/pull/88220 [rust#85835]: https://github.com/rust-lang/rust/pull/85835 -[rust#86879]: https://github.com/rust-lang/rust/pull/86879 [rust#86744]: https://github.com/rust-lang/rust/pull/86744 -[rust#84662]: https://github.com/rust-lang/rust/pull/84662 -[rust#86593]: https://github.com/rust-lang/rust/pull/86593 -[rust#81050]: https://github.com/rust-lang/rust/pull/81050 [rust#81363]: https://github.com/rust-lang/rust/pull/81363 [rust#84111]: https://github.com/rust-lang/rust/pull/84111 [rust#85769]: https://github.com/rust-lang/rust/pull/85769#issuecomment-854363720 -[rust#88490]: https://github.com/rust-lang/rust/pull/88490 -[rust#88269]: https://github.com/rust-lang/rust/pull/88269 -[rust#84176]: https://github.com/rust-lang/rust/pull/84176 [rust#88399]: https://github.com/rust-lang/rust/pull/88399 -[rust#88227]: https://github.com/rust-lang/rust/pull/88227 -[rust#88200]: https://github.com/rust-lang/rust/pull/88200 -[rust#82776]: https://github.com/rust-lang/rust/pull/82776 -[rust#88077]: https://github.com/rust-lang/rust/pull/88077 -[rust#87728]: https://github.com/rust-lang/rust/pull/87728 -[rust#87050]: https://github.com/rust-lang/rust/pull/87050 -[rust#87619]: https://github.com/rust-lang/rust/pull/87619 [rust#81825]: https://github.com/rust-lang/rust/pull/81825#issuecomment-808406918 [rust#88019]: https://github.com/rust-lang/rust/pull/88019 [rust#87666]: https://github.com/rust-lang/rust/pull/87666 @@ -575,20 +520,14 @@ Compatibility Notes [86294]: https://github.com/rust-lang/rust/pull/86294 [86858]: https://github.com/rust-lang/rust/pull/86858 [86761]: https://github.com/rust-lang/rust/pull/86761 -[85769]: https://github.com/rust-lang/rust/pull/85769 [85746]: https://github.com/rust-lang/rust/pull/85746 -[85305]: https://github.com/rust-lang/rust/pull/85305 [85270]: https://github.com/rust-lang/rust/pull/85270 -[84111]: https://github.com/rust-lang/rust/pull/84111 [83918]: https://github.com/rust-lang/rust/pull/83918 [79965]: https://github.com/rust-lang/rust/pull/79965 -[87370]: https://github.com/rust-lang/rust/pull/87370 -[87298]: https://github.com/rust-lang/rust/pull/87298 [cargo/9663]: https://github.com/rust-lang/cargo/pull/9663 [cargo/9675]: https://github.com/rust-lang/cargo/pull/9675 [cargo/9550]: https://github.com/rust-lang/cargo/pull/9550 [cargo/9680]: https://github.com/rust-lang/cargo/pull/9680 -[cargo/9663]: https://github.com/rust-lang/cargo/pull/9663 [`array::map`]: https://doc.rust-lang.org/stable/std/primitive.array.html#method.map [`Bound::cloned`]: https://doc.rust-lang.org/stable/std/ops/enum.Bound.html#method.cloned [`Drain::as_str`]: https://doc.rust-lang.org/stable/std/string/struct.Drain.html#method.as_str @@ -597,7 +536,6 @@ Compatibility Notes [`MaybeUninit::assume_init_mut`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_mut [`MaybeUninit::assume_init_ref`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_ref [`MaybeUninit::write`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.write -[`Seek::rewind`]: https://doc.rust-lang.org/stable/std/io/trait.Seek.html#method.rewind [`ops::ControlFlow`]: https://doc.rust-lang.org/stable/std/ops/enum.ControlFlow.html [`str::from_utf8_unchecked`]: https://doc.rust-lang.org/stable/std/str/fn.from_utf8_unchecked.html [`x86::_bittest`]: https://doc.rust-lang.org/stable/core/arch/x86/fn._bittest.html @@ -701,7 +639,6 @@ Compatibility Notes [85574]: https://github.com/rust-lang/rust/issues/85574 [86831]: https://github.com/rust-lang/rust/issues/86831 [86063]: https://github.com/rust-lang/rust/issues/86063 -[86831]: https://github.com/rust-lang/rust/issues/86831 [79608]: https://github.com/rust-lang/rust/pull/79608 [84988]: https://github.com/rust-lang/rust/pull/84988 [84701]: https://github.com/rust-lang/rust/pull/84701 @@ -903,7 +840,6 @@ related tools. [`Ordering::is_le`]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.is_le [`Ordering::is_lt`]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.is_lt [`Ordering::is_ne`]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.is_ne -[`OsStr::eq_ignore_ascii_case`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.eq_ignore_ascii_case [`OsStr::is_ascii`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.is_ascii [`OsStr::make_ascii_lowercase`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.make_ascii_lowercase [`OsStr::make_ascii_uppercase`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.make_ascii_uppercase @@ -1234,7 +1170,6 @@ Internal Only [80053]: https://github.com/rust-lang/rust/pull/80053 [79502]: https://github.com/rust-lang/rust/pull/79502 [75180]: https://github.com/rust-lang/rust/pull/75180 -[79135]: https://github.com/rust-lang/rust/pull/79135 [81521]: https://github.com/rust-lang/rust/pull/81521 [80968]: https://github.com/rust-lang/rust/pull/80968 [80959]: https://github.com/rust-lang/rust/pull/80959 @@ -1548,7 +1483,6 @@ related tools. [`slice::select_nth_unstable`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable [`slice::select_nth_unstable_by`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable_by [`slice::select_nth_unstable_by_key`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable_by_key -[`hint::spin_loop`]: https://doc.rust-lang.org/stable/std/hint/fn.spin_loop.html [`Poll::is_ready`]: https://doc.rust-lang.org/stable/std/task/enum.Poll.html#method.is_ready [`Poll::is_pending`]: https://doc.rust-lang.org/stable/std/task/enum.Poll.html#method.is_pending [rustdoc-ws-post]: https://blog.guillaume-gomez.fr/articles/2020-11-11+New+doc+comment+handling+in+rustdoc @@ -1795,8 +1729,6 @@ Internal Only [74869]: https://github.com/rust-lang/rust/pull/74869/ [73858]: https://github.com/rust-lang/rust/pull/73858/ [75716]: https://github.com/rust-lang/rust/pull/75716/ -[75908]: https://github.com/rust-lang/rust/pull/75908/ -[75516]: https://github.com/rust-lang/rust/pull/75516/ [75560]: https://github.com/rust-lang/rust/pull/75560/ [75568]: https://github.com/rust-lang/rust/pull/75568/ [75366]: https://github.com/rust-lang/rust/pull/75366/ @@ -1811,7 +1743,6 @@ Internal Only [73583]: https://github.com/rust-lang/rust/pull/73583/ [73084]: https://github.com/rust-lang/rust/pull/73084/ [73197]: https://github.com/rust-lang/rust/pull/73197/ -[72488]: https://github.com/rust-lang/rust/pull/72488/ [cargo/8456]: https://github.com/rust-lang/cargo/pull/8456/ [cargo/8478]: https://github.com/rust-lang/cargo/pull/8478/ [cargo/8485]: https://github.com/rust-lang/cargo/pull/8485/ @@ -1822,7 +1753,6 @@ Internal Only [`RangeInclusive::is_empty`]: https://doc.rust-lang.org/nightly/std/ops/struct.RangeInclusive.html#method.is_empty [`Result::as_deref_mut`]: https://doc.rust-lang.org/nightly/std/result/enum.Result.html#method.as_deref_mut [`Result::as_deref`]: https://doc.rust-lang.org/nightly/std/result/enum.Result.html#method.as_deref -[`TypeId::of`]: https://doc.rust-lang.org/nightly/std/any/struct.TypeId.html#method.of [`Vec::leak`]: https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.leak [`f32::TAU`]: https://doc.rust-lang.org/nightly/std/f32/consts/constant.TAU.html [`f64::TAU`]: https://doc.rust-lang.org/nightly/std/f64/consts/constant.TAU.html @@ -2806,7 +2736,6 @@ Compatibility Notes [63803]: https://github.com/rust-lang/rust/pull/63803/ [cargo/7450]: https://github.com/rust-lang/cargo/pull/7450/ [cargo/7507]: https://github.com/rust-lang/cargo/pull/7507/ -[cargo/7525]: https://github.com/rust-lang/cargo/pull/7525/ [cargo/7333]: https://github.com/rust-lang/cargo/pull/7333/ [(rfc 2008)]: https://rust-lang.github.io/rfcs/2008-non-exhaustive.html [`f32::to_be_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_be_bytes @@ -2939,13 +2868,6 @@ Compatibility Notes [63786]: https://github.com/rust-lang/rust/pull/63786/ [63827]: https://github.com/rust-lang/rust/pull/63827/ [63834]: https://github.com/rust-lang/rust/pull/63834/ -[63927]: https://github.com/rust-lang/rust/pull/63927/ -[63933]: https://github.com/rust-lang/rust/pull/63933/ -[63934]: https://github.com/rust-lang/rust/pull/63934/ -[63938]: https://github.com/rust-lang/rust/pull/63938/ -[63940]: https://github.com/rust-lang/rust/pull/63940/ -[63941]: https://github.com/rust-lang/rust/pull/63941/ -[63945]: https://github.com/rust-lang/rust/pull/63945/ [64010]: https://github.com/rust-lang/rust/pull/64010/ [64028]: https://github.com/rust-lang/rust/pull/64028/ [64334]: https://github.com/rust-lang/rust/pull/64334/ @@ -3174,7 +3096,6 @@ Compatibility Notes [`Cell<slice>::as_slice_of_cells`]: https://doc.rust-lang.org/std/cell/struct.Cell.html#method.as_slice_of_cells [`DoubleEndedIterator::nth_back`]: https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html#method.nth_back [`Option::xor`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.xor -[`RefCell::try_borrow_unguarded`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.try_borrow_unguarded [`Wrapping::reverse_bits`]: https://doc.rust-lang.org/std/num/struct.Wrapping.html#method.reverse_bits [`i128::reverse_bits`]: https://doc.rust-lang.org/std/primitive.i128.html#method.reverse_bits [`i16::reverse_bits`]: https://doc.rust-lang.org/std/primitive.i16.html#method.reverse_bits @@ -3673,7 +3594,6 @@ Compatibility Notes - [Libtest no longer creates a new thread for each test when `--test-threads=1`. It also runs the tests in deterministic order][56243] -[55982]: https://github.com/rust-lang/rust/pull/55982/ [56243]: https://github.com/rust-lang/rust/pull/56243 [56303]: https://github.com/rust-lang/rust/pull/56303/ [56351]: https://github.com/rust-lang/rust/pull/56351/ @@ -4073,7 +3993,6 @@ Cargo [52813]: https://github.com/rust-lang/rust/pull/52813/ [53218]: https://github.com/rust-lang/rust/pull/53218/ -[53555]: https://github.com/rust-lang/rust/issues/53555/ [54057]: https://github.com/rust-lang/rust/pull/54057/ [54240]: https://github.com/rust-lang/rust/pull/54240/ [54430]: https://github.com/rust-lang/rust/pull/54430/ @@ -4195,7 +4114,6 @@ Misc [53044]: https://github.com/rust-lang/rust/pull/53044/ [53165]: https://github.com/rust-lang/rust/pull/53165/ [53611]: https://github.com/rust-lang/rust/pull/53611/ -[53213]: https://github.com/rust-lang/rust/pull/53213/ [53236]: https://github.com/rust-lang/rust/pull/53236/ [53272]: https://github.com/rust-lang/rust/pull/53272/ [53370]: https://github.com/rust-lang/rust/pull/53370/ @@ -4203,7 +4121,6 @@ Misc [53774]: https://github.com/rust-lang/rust/pull/53774/ [53822]: https://github.com/rust-lang/rust/pull/53822/ [54057]: https://github.com/rust-lang/rust/pull/54057/ -[54146]: https://github.com/rust-lang/rust/pull/54146/ [54404]: https://github.com/rust-lang/rust/pull/54404/ [cargo/5877]: https://github.com/rust-lang/cargo/pull/5877/ [cargo/5878]: https://github.com/rust-lang/cargo/pull/5878/ @@ -4311,12 +4228,10 @@ Compatibility Notes [52330]: https://github.com/rust-lang/rust/pull/52330/ [52354]: https://github.com/rust-lang/rust/pull/52354/ [52402]: https://github.com/rust-lang/rust/pull/52402/ -[52103]: https://github.com/rust-lang/rust/pull/52103/ [52197]: https://github.com/rust-lang/rust/pull/52197/ [51807]: https://github.com/rust-lang/rust/pull/51807/ [51899]: https://github.com/rust-lang/rust/pull/51899/ [51912]: https://github.com/rust-lang/rust/pull/51912/ -[51511]: https://github.com/rust-lang/rust/pull/51511/ [51619]: https://github.com/rust-lang/rust/pull/51619/ [51656]: https://github.com/rust-lang/rust/pull/51656/ [51178]: https://github.com/rust-lang/rust/pull/51178/ @@ -4456,7 +4371,6 @@ Compatibility Notes [50855]: https://github.com/rust-lang/rust/pull/50855/ [51050]: https://github.com/rust-lang/rust/pull/51050/ [51196]: https://github.com/rust-lang/rust/pull/51196/ -[51200]: https://github.com/rust-lang/rust/pull/51200/ [51241]: https://github.com/rust-lang/rust/pull/51241/ [51276]: https://github.com/rust-lang/rust/pull/51276/ [51298]: https://github.com/rust-lang/rust/pull/51298/ @@ -4637,15 +4551,12 @@ Compatibility Notes [49664]: https://github.com/rust-lang/rust/pull/49664/ [49699]: https://github.com/rust-lang/rust/pull/49699/ [49707]: https://github.com/rust-lang/rust/pull/49707/ -[49719]: https://github.com/rust-lang/rust/pull/49719/ [49896]: https://github.com/rust-lang/rust/pull/49896/ [49968]: https://github.com/rust-lang/rust/pull/49968/ [50163]: https://github.com/rust-lang/rust/pull/50163 [50177]: https://github.com/rust-lang/rust/pull/50177/ [50378]: https://github.com/rust-lang/rust/pull/50378/ -[50398]: https://github.com/rust-lang/rust/pull/50398/ [50423]: https://github.com/rust-lang/rust/pull/50423/ -[cargo/5203]: https://github.com/rust-lang/cargo/pull/5203/ [cargo/5335]: https://github.com/rust-lang/cargo/pull/5335/ [cargo/5359]: https://github.com/rust-lang/cargo/pull/5359/ [cargo/5360]: https://github.com/rust-lang/cargo/pull/5360/ @@ -4847,7 +4758,6 @@ Compatibility Notes [47813]: https://github.com/rust-lang/rust/pull/47813 [48056]: https://github.com/rust-lang/rust/pull/48056 [48125]: https://github.com/rust-lang/rust/pull/48125 -[48166]: https://github.com/rust-lang/rust/pull/48166 [48235]: https://github.com/rust-lang/rust/pull/48235 [48274]: https://github.com/rust-lang/rust/pull/48274 [48281]: https://github.com/rust-lang/rust/pull/48281 @@ -4864,10 +4774,7 @@ Compatibility Notes [48978]: https://github.com/rust-lang/rust/pull/48978 [49101]: https://github.com/rust-lang/rust/pull/49101 [49109]: https://github.com/rust-lang/rust/pull/49109 -[49121]: https://github.com/rust-lang/rust/pull/49121 [49162]: https://github.com/rust-lang/rust/pull/49162 -[49184]: https://github.com/rust-lang/rust/pull/49184 -[49234]: https://github.com/rust-lang/rust/pull/49234 [49255]: https://github.com/rust-lang/rust/pull/49255 [49299]: https://github.com/rust-lang/rust/pull/49299 [49305]: https://github.com/rust-lang/rust/pull/49305 @@ -5114,7 +5021,6 @@ Compatibility Notes [44884]: https://github.com/rust-lang/rust/pull/44884 [45198]: https://github.com/rust-lang/rust/pull/45198 [45506]: https://github.com/rust-lang/rust/pull/45506 -[45904]: https://github.com/rust-lang/rust/pull/45904 [45990]: https://github.com/rust-lang/rust/pull/45990 [46012]: https://github.com/rust-lang/rust/pull/46012 [46077]: https://github.com/rust-lang/rust/pull/46077 @@ -5126,7 +5032,6 @@ Compatibility Notes [46671]: https://github.com/rust-lang/rust/pull/46671 [46713]: https://github.com/rust-lang/rust/pull/46713 [46735]: https://github.com/rust-lang/rust/pull/46735 -[46749]: https://github.com/rust-lang/rust/pull/46749 [46760]: https://github.com/rust-lang/rust/pull/46760 [46798]: https://github.com/rust-lang/rust/pull/46798 [46828]: https://github.com/rust-lang/rust/pull/46828 @@ -5297,7 +5202,6 @@ Compatibility Notes [42526]: https://github.com/rust-lang/rust/pull/42526 -[43017]: https://github.com/rust-lang/rust/pull/43017 [43716]: https://github.com/rust-lang/rust/pull/43716 [43949]: https://github.com/rust-lang/rust/pull/43949 [44015]: https://github.com/rust-lang/rust/pull/44015 @@ -5527,8 +5431,6 @@ Cargo - [Added `--no-fail-fast` flag to cargo to run all benchmarks regardless of failure.][cargo/4248] - [Changed the convention around which file is the crate root.][cargo/4259] -- [The `include`/`exclude` property in `Cargo.toml` now accepts gitignore paths - instead of glob patterns][cargo/4270]. Glob patterns are now deprecated. Compatibility Notes ------------------- @@ -5571,7 +5473,6 @@ Compatibility Notes [cargo/4229]: https://github.com/rust-lang/cargo/pull/4229 [cargo/4248]: https://github.com/rust-lang/cargo/pull/4248 [cargo/4259]: https://github.com/rust-lang/cargo/pull/4259 -[cargo/4270]: https://github.com/rust-lang/cargo/pull/4270 [`CStr::into_c_string`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.into_c_string [`CString::as_c_str`]: https://doc.rust-lang.org/std/ffi/struct.CString.html#method.as_c_str [`CString::into_boxed_c_str`]: https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_boxed_c_str @@ -5864,7 +5765,6 @@ Misc ---- - [rustdoc can now use pulldown-cmark with the `--enable-commonmark` flag][40338] -- [Added rust-windbg script for better debugging on Windows][39983] - [Rust now uses the official cross compiler for NetBSD][40612] - [rustdoc now accepts `#` at the start of files][40828] - [Fixed jemalloc support for musl][41168] @@ -5899,7 +5799,6 @@ Compatibility Notes [38165]: https://github.com/rust-lang/rust/pull/38165 [39799]: https://github.com/rust-lang/rust/pull/39799 [39891]: https://github.com/rust-lang/rust/pull/39891 -[39983]: https://github.com/rust-lang/rust/pull/39983 [40043]: https://github.com/rust-lang/rust/pull/40043 [40241]: https://github.com/rust-lang/rust/pull/40241 [40338]: https://github.com/rust-lang/rust/pull/40338 @@ -6195,7 +6094,6 @@ Compatibility Notes [cargo/3691]: https://github.com/rust-lang/cargo/pull/3691 [cargo/3699]: https://github.com/rust-lang/cargo/pull/3699 [cargo/3731]: https://github.com/rust-lang/cargo/pull/3731 -[mdbook]: https://crates.io/crates/mdbook [ubook]: https://doc.rust-lang.org/unstable-book/ @@ -6266,7 +6164,7 @@ Libraries * [Ctrl-Z returns from `Stdin.read()` when reading from the console on Windows][38274] * [std: Fix partial writes in `LineWriter`][38062] -* [std: Clamp max read/write sizes on Unix][38062] +* [std: Clamp max read/write sizes on Unix][38622] * [Use more specific panic message for `&str` slicing errors][38066] * [`TcpListener::set_only_v6` is deprecated][38304]. This functionality cannot be achieved in std currently. @@ -6332,7 +6230,7 @@ Compatibility Notes [38006]: https://github.com/rust-lang/rust/pull/38006 [38051]: https://github.com/rust-lang/rust/pull/38051 [38062]: https://github.com/rust-lang/rust/pull/38062 -[38062]: https://github.com/rust-lang/rust/pull/38622 +[38622]: https://github.com/rust-lang/rust/pull/38622 [38066]: https://github.com/rust-lang/rust/pull/38066 [38069]: https://github.com/rust-lang/rust/pull/38069 [38131]: https://github.com/rust-lang/rust/pull/38131 @@ -6340,7 +6238,6 @@ Compatibility Notes [38274]: https://github.com/rust-lang/rust/pull/38274 [38304]: https://github.com/rust-lang/rust/pull/38304 [38313]: https://github.com/rust-lang/rust/pull/38313 -[38314]: https://github.com/rust-lang/rust/pull/38314 [38327]: https://github.com/rust-lang/rust/pull/38327 [38401]: https://github.com/rust-lang/rust/pull/38401 [38413]: https://github.com/rust-lang/rust/pull/38413 @@ -6390,7 +6287,6 @@ Compatibility Notes [cargo/3546]: https://github.com/rust-lang/cargo/pull/3546 [cargo/3557]: https://github.com/rust-lang/cargo/pull/3557 [cargo/3604]: https://github.com/rust-lang/cargo/pull/3604 -[RFC 1623]: https://github.com/rust-lang/rfcs/blob/master/text/1623-static.md Version 1.15.1 (2017-02-09) @@ -6605,7 +6501,6 @@ Compatibility Notes [38192]: https://github.com/rust-lang/rust/pull/38192 [38279]: https://github.com/rust-lang/rust/pull/38279 [38835]: https://github.com/rust-lang/rust/pull/38835 -[RFC 1492]: https://github.com/rust-lang/rfcs/blob/master/text/1492-dotdot-in-patterns.md [RFC 1506]: https://github.com/rust-lang/rfcs/blob/master/text/1506-adt-kinds.md [RFC 1560]: https://github.com/rust-lang/rfcs/blob/master/text/1560-name-resolution.md [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md @@ -6794,7 +6689,6 @@ Compatibility Notes [1.14wasm]: https://users.rust-lang.org/t/compiling-to-the-web-with-rust-and-emscripten/7627 [36430]: https://github.com/rust-lang/rust/pull/36430 [36595]: https://github.com/rust-lang/rust/pull/36595 -[36595]: https://github.com/rust-lang/rust/pull/36595 [36692]: https://github.com/rust-lang/rust/pull/36692 [36767]: https://github.com/rust-lang/rust/pull/36767 [36794]: https://github.com/rust-lang/rust/pull/36794 @@ -7022,7 +6916,6 @@ Compatibility Notes [34623]: https://github.com/rust-lang/rust/pull/34623 [34923]: https://github.com/rust-lang/rust/pull/34923 [34942]: https://github.com/rust-lang/rust/pull/34942 -[34982]: https://github.com/rust-lang/rust/pull/34982 [35021]: https://github.com/rust-lang/rust/pull/35021 [35048]: https://github.com/rust-lang/rust/pull/35048 [35074]: https://github.com/rust-lang/rust/pull/35074 @@ -7079,7 +6972,6 @@ Compatibility Notes [36586]: https://github.com/rust-lang/rust/pull/36586 [36592]: https://github.com/rust-lang/rust/pull/36592 [36631]: https://github.com/rust-lang/rust/pull/36631 -[36639]: https://github.com/rust-lang/rust/pull/36639 [36721]: https://github.com/rust-lang/rust/pull/36721 [36727]: https://github.com/rust-lang/rust/pull/36727 [36730]: https://github.com/rust-lang/rust/pull/36730 @@ -7111,7 +7003,6 @@ Compatibility Notes [cargo/3205]: https://github.com/rust-lang/cargo/pull/3205 [cargo/3241]: https://github.com/rust-lang/cargo/pull/3241 [cargo/3242]: https://github.com/rust-lang/cargo/pull/3242 -[rustup]: https://www.rustup.rs [`checked_abs`]: https://doc.rust-lang.org/std/primitive.i32.html#method.checked_abs [`wrapping_abs`]: https://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_abs [`overflowing_abs`]: https://doc.rust-lang.org/std/primitive.i32.html#method.overflowing_abs @@ -8029,7 +7920,7 @@ Cargo targets can be specified together. [RFC 1361]. * [The environment variables `CARGO_TARGET_ROOT`, `RUSTC`, and `RUSTDOC` take precedence over the `build.target-dir`, - `build.rustc`, and `build.rustdoc` configuration values][1.8cv]. + `build.rustc`, and `build.rustdoc` configuration values][1.8cfv]. * [The child process tree is killed on Windows when Cargo is killed][1.8ck]. * [The `build.target` configuration value sets the target platform, @@ -8079,7 +7970,7 @@ Compatibility Notes [1.8ck]: https://github.com/rust-lang/cargo/pull/2370 [1.8ct]: https://github.com/rust-lang/cargo/pull/2335 [1.8cu]: https://github.com/rust-lang/rust/pull/31390 -[1.8cv]: https://github.com/rust-lang/cargo/issues/2365 +[1.8cfv]: https://github.com/rust-lang/cargo/issues/2365 [1.8cv]: https://github.com/rust-lang/rust/pull/30998 [1.8h]: https://github.com/rust-lang/rust/pull/31460 [1.8l]: https://github.com/rust-lang/rust/pull/31668 @@ -9002,13 +8893,13 @@ Misc * The compiler gained many new extended error descriptions, which can be accessed with the `--explain` flag. * The `dropck` pass, which checks that destructors can't access - destroyed values, [has been rewritten][dropck]. This fixes some + destroyed values, [has been rewritten][27261]. This fixes some soundness holes, and as such will cause some previously-compiling code to no longer build. * `rustc` now uses [LLVM to write archive files where possible][ar]. Eventually this will eliminate the compiler's dependency on the ar utility. -* Rust has [preliminary support for i686 FreeBSD][fb] (it has long +* Rust has [preliminary support for i686 FreeBSD][26959] (it has long supported FreeBSD on x86_64). * The [`unused_mut`][lum], [`unconditional_recursion`][lur], [`improper_ctypes`][lic], and [`negate_unsigned`][lnu] lints are @@ -9047,7 +8938,7 @@ Misc [ar]: https://github.com/rust-lang/rust/pull/26926 [b14]: https://static.rust-lang.org/dist/rust-beta-x86_64-pc-windows-msvc.msi [dms]: https://github.com/rust-lang/rust/pull/26241 -[dropck]: https://github.com/rust-lang/rust/pull/27261 +[27261]: https://github.com/rust-lang/rust/pull/27261 [dropckrfc]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md [ds]: https://github.com/rust-lang/rust/pull/26818 [dst1]: http://doc.rust-lang.org/nightly/std/mem/fn.size_of_val.html @@ -9055,9 +8946,8 @@ Misc [dst3]: https://github.com/rust-lang/rust/pull/27351 [e]: https://github.com/rust-lang/rust/pull/24793 [f]: https://github.com/rust-lang/rust/pull/26588 -[fb]: https://github.com/rust-lang/rust/pull/26959 +[26959]: https://github.com/rust-lang/rust/pull/26959 [fl]: https://github.com/rust-lang/rust-installer/pull/41 -[hs]: http://doc.rust-lang.org/nightly/std/hash/trait.Hash.html#method.hash_slice [ie]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html [iec]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.cause [iegm]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.get_mut @@ -9328,7 +9218,7 @@ Misc to rustc. * [Android executables are always position independent][pie]. * [The `drop_with_repr_extern` lint warns about mixing `repr(C)` - with `Drop`][drop]. + with `Drop`][24935]. [`str::split_whitespace`]: https://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_whitespace [`FromRawFd`]: https://doc.rust-lang.org/nightly/std/os/unix/io/trait.FromRawFd.html @@ -9358,7 +9248,7 @@ Misc [`BinaryHeap`]: https://doc.rust-lang.org/nightly/std/collections/struct.BinaryHeap.html [ll]: https://github.com/rust-lang/rust/pull/26022 [`split_off`]: https://doc.rust-lang.org/nightly/collections/linked_list/struct.LinkedList.html#method.split_off -[drop]: https://github.com/rust-lang/rust/pull/24935 +[24935]: https://github.com/rust-lang/rust/pull/24935 Version 1.0.0 (2015-05-15) ======================== @@ -9411,7 +9301,7 @@ Language property: generic code cannot behave differently for different type arguments except in minor ways. * The `unsafe_destructor` feature is now deprecated in favor of the - [new `dropck`][dropck]. This change is a major reduction in unsafe + [new `dropck`][rfc769]. This change is a major reduction in unsafe code. Libraries @@ -9419,7 +9309,7 @@ Libraries * The `thread_local` module [has been renamed to `std::thread`][th]. * The methods of `IteratorExt` [have been moved to the `Iterator` - trait itself][ie]. + trait itself][23300]. * Several traits that implement Rust's conventions for type conversions, `AsMut`, `AsRef`, `From`, and `Into` have been [centralized in the `std::convert` module][con]. @@ -9438,7 +9328,7 @@ Libraries * [In method resolution, object methods are resolved before inherent methods][meth]. * [`String::from_str` has been deprecated in favor of the `From` impl, - `String::from`][sf]. + `String::from`][24517]. * [`io::Error` implements `Sync`][ios]. * [The `words` method on `&str` has been replaced with `split_whitespace`][sw], to avoid answering the tricky question, 'what is @@ -9486,7 +9376,7 @@ Misc [con]: https://github.com/rust-lang/rust/pull/23875 [cr]: https://github.com/rust-lang/rust/pull/23419 [fe]: https://github.com/rust-lang/rust/pull/23879 -[ie]: https://github.com/rust-lang/rust/pull/23300 +[23300]: https://github.com/rust-lang/rust/pull/23300 [inv]: https://github.com/rust-lang/rust/pull/23938 [ios]: https://github.com/rust-lang/rust/pull/24133 [lex]: https://github.com/rust-lang/rfcs/blob/master/text/0879-small-base-lexing.md @@ -9494,7 +9384,7 @@ Misc [meth]: https://github.com/rust-lang/rust/pull/24056 [pat]: https://github.com/rust-lang/rfcs/blob/master/text/0528-string-patterns.md [po]: https://github.com/rust-lang/rust/pull/24270 -[sf]: https://github.com/rust-lang/rust/pull/24517 +[24517]: https://github.com/rust-lang/rust/pull/24517 [slp]: https://github.com/rust-lang/rust/pull/23949 [spl]: https://github.com/rust-lang/rfcs/blob/master/text/0979-align-splitn-with-other-languages.md [sw]: https://github.com/rust-lang/rfcs/blob/master/text/1054-str-words.md @@ -9512,7 +9402,7 @@ Misc [conversion]: https://github.com/rust-lang/rfcs/pull/529 [num-traits]: https://github.com/rust-lang/rust/pull/23549 [index-value]: https://github.com/rust-lang/rust/pull/23601 -[dropck]: https://github.com/rust-lang/rfcs/pull/769 +[rfc769]: https://github.com/rust-lang/rfcs/pull/769 [ci-compare]: https://gist.github.com/brson/a30a77836fbec057cbee [fn-inherit]: https://github.com/rust-lang/rust/pull/23282 [fn-blanket]: https://github.com/rust-lang/rust/pull/23895 diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2fb5a0f9faf82..5703a72c686e5 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,6 +1,7 @@ //! A helper class for dealing with static archives -use std::ffi::{CStr, CString}; +use std::env; +use std::ffi::{CStr, CString, OsString}; use std::io; use std::mem; use std::path::{Path, PathBuf}; @@ -158,54 +159,127 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { output_path.with_extension("lib") }; - // we've checked for \0 characters in the library name already - let dll_name_z = CString::new(lib_name).unwrap(); - // All import names are Rust identifiers and therefore cannot contain \0 characters. - // FIXME: when support for #[link_name] implemented, ensure that import.name values don't - // have any \0 characters - let import_name_and_ordinal_vector: Vec<(CString, Option<u16>)> = dll_imports + let mingw_gnu_toolchain = self.config.sess.target.llvm_target.ends_with("pc-windows-gnu"); + + let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports .iter() .map(|import: &DllImport| { if self.config.sess.target.arch == "x86" { - (LlvmArchiveBuilder::i686_decorated_name(import), import.ordinal) + ( + LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain), + import.ordinal, + ) } else { - (CString::new(import.name.to_string()).unwrap(), import.ordinal) + (import.name.to_string(), import.ordinal) } }) .collect(); - let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + if mingw_gnu_toolchain { + // The binutils linker used on -windows-gnu targets cannot read the import + // libraries generated by LLVM: in our attempts, the linker produced an .EXE + // that loaded but crashed with an AV upon calling one of the imported + // functions. Therefore, use binutils to create the import library instead, + // by writing a .DEF file to the temp dir and calling binutils's dlltool. + let def_file_path = + tmpdir.as_ref().join(format!("{}_imports", lib_name)).with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{} @{} NONAME", name, n), + None => name, + } + }) + .collect::<Vec<String>>() + .join("\n") + ); - tracing::trace!("invoking LLVMRustWriteImportLibrary"); - tracing::trace!(" dll_name {:#?}", dll_name_z); - tracing::trace!(" output_path {}", output_path.display()); - tracing::trace!( - " import names: {}", - dll_imports.iter().map(|import| import.name.to_string()).collect::<Vec<_>>().join(", "), - ); + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + self.config.sess.fatal(&format!("Error writing .DEF file: {}", e)); + } + }; - let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_and_ordinal_vector - .iter() - .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) - .collect(); - let result = unsafe { - crate::llvm::LLVMRustWriteImportLibrary( - dll_name_z.as_ptr(), - output_path_z.as_ptr(), - ffi_exports.as_ptr(), - ffi_exports.len(), - llvm_machine_type(&self.config.sess.target.arch) as u16, - !self.config.sess.target.is_like_msvc, - ) - }; + let dlltool = find_binutils_dlltool(self.config.sess); + let result = std::process::Command::new(dlltool) + .args([ + "-d", + def_file_path.to_str().unwrap(), + "-D", + lib_name, + "-l", + output_path.to_str().unwrap(), + ]) + .output(); + + match result { + Err(e) => { + self.config.sess.fatal(&format!("Error calling dlltool: {}", e.to_string())); + } + Ok(output) if !output.status.success() => self.config.sess.fatal(&format!( + "Dlltool could not create import library: {}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + )), + _ => {} + } + } else { + // we've checked for \0 characters in the library name already + let dll_name_z = CString::new(lib_name).unwrap(); + + let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + + tracing::trace!("invoking LLVMRustWriteImportLibrary"); + tracing::trace!(" dll_name {:#?}", dll_name_z); + tracing::trace!(" output_path {}", output_path.display()); + tracing::trace!( + " import names: {}", + dll_imports + .iter() + .map(|import| import.name.to_string()) + .collect::<Vec<_>>() + .join(", "), + ); - if result == crate::llvm::LLVMRustResult::Failure { - self.config.sess.fatal(&format!( - "Error creating import library for {}: {}", - lib_name, - llvm::last_error().unwrap_or("unknown LLVM error".to_string()) - )); - } + // All import names are Rust identifiers and therefore cannot contain \0 characters. + // FIXME: when support for #[link_name] is implemented, ensure that the import names + // still don't contain any \0 characters. Also need to check that the names don't + // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved + // in definition files. + let cstring_import_name_and_ordinal_vector: Vec<(CString, Option<u16>)> = + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| (CString::new(name).unwrap(), ordinal)) + .collect(); + + let ffi_exports: Vec<LLVMRustCOFFShortExport> = cstring_import_name_and_ordinal_vector + .iter() + .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) + .collect(); + let result = unsafe { + crate::llvm::LLVMRustWriteImportLibrary( + dll_name_z.as_ptr(), + output_path_z.as_ptr(), + ffi_exports.as_ptr(), + ffi_exports.len(), + llvm_machine_type(&self.config.sess.target.arch) as u16, + !self.config.sess.target.is_like_msvc, + ) + }; + + if result == crate::llvm::LLVMRustResult::Failure { + self.config.sess.fatal(&format!( + "Error creating import library for {}: {}", + lib_name, + llvm::last_error().unwrap_or("unknown LLVM error".to_string()) + )); + } + }; self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { self.config.sess.fatal(&format!( @@ -332,22 +406,61 @@ impl<'a> LlvmArchiveBuilder<'a> { } } - fn i686_decorated_name(import: &DllImport) -> CString { + fn i686_decorated_name(import: &DllImport, mingw: bool) -> String { let name = import.name; - // We verified during construction that `name` does not contain any NULL characters, so the - // conversion to CString is guaranteed to succeed. - CString::new(match import.calling_convention { - DllCallingConvention::C => format!("_{}", name), - DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size), + let prefix = if mingw { "" } else { "_" }; + + match import.calling_convention { + DllCallingConvention::C => format!("{}{}", prefix, name), + DllCallingConvention::Stdcall(arg_list_size) => { + format!("{}{}@{}", prefix, name, arg_list_size) + } DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size), DllCallingConvention::Vectorcall(arg_list_size) => { format!("{}@@{}", name, arg_list_size) } - }) - .unwrap() + } } } fn string_to_io_error(s: String) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) } + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.debugging_opts.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let mut tool_name: OsString = if sess.host.arch != sess.target.arch { + // We are cross-compiling, so we need the tool with the prefix matching our target + if sess.target.arch == "x86" { + "i686-w64-mingw32-dlltool" + } else { + "x86_64-w64-mingw32-dlltool" + } + } else { + // We are not cross-compiling, so we just want `dlltool` + "dlltool" + } + .into(); + + if sess.host.options.is_like_windows { + // If we're compiling on Windows, add the .exe suffix + tool_name.push(".exe"); + } + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 04e04e297cdd2..d3664e53447d0 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -10,7 +10,6 @@ pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; -use hir::def_id::CRATE_DEF_ID; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; @@ -291,7 +290,12 @@ pub struct InferCtxt<'a, 'tcx> { /// The `DefId` of the item in whose context we are performing inference or typeck. /// It is used to check whether an opaque type use is a defining use. - pub defining_use_anchor: LocalDefId, + /// + /// If it is `None`, we can't resolve opaque types here and need to bubble up + /// the obligation. This frequently happens for + /// short lived InferCtxt within queries. The opaque type obligations are forwarded + /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. + pub defining_use_anchor: Option<LocalDefId>, /// During type-checking/inference of a body, `in_progress_typeck_results` /// contains a reference to the typeck results being built up, which are @@ -547,7 +551,7 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, - defining_use_anchor: LocalDefId, + defining_use_anchor: Option<LocalDefId>, } pub trait TyCtxtInferExt<'tcx> { @@ -556,11 +560,7 @@ pub trait TyCtxtInferExt<'tcx> { impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { - InferCtxtBuilder { - tcx: self, - defining_use_anchor: CRATE_DEF_ID, - fresh_typeck_results: None, - } + InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None } } } @@ -580,7 +580,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used /// in mir borrowck. pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self { - self.defining_use_anchor = defining_use_anchor; + self.defining_use_anchor = Some(defining_use_anchor); self } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index c2ef0b41e27bf..11153fbe7896b 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -328,6 +328,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, }); } + + fn opaque_type_origin(&self, def_id: LocalDefId) -> Option<hir::OpaqueTyOrigin> { + let tcx = self.tcx; + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let parent_def_id = self.defining_use_anchor?; + let item_kind = &tcx.hir().expect_item(def_id).kind; + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { + span_bug!( + tcx.def_span(def_id), + "weird opaque type: {:#?}", + item_kind + ) + }; + let in_definition_scope = match *origin { + // Async `impl Trait` + hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, + // Anonymous `impl Trait` + hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, + // Named `type Foo = impl Bar;` + hir::OpaqueTyOrigin::TyAlias => { + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) + } + }; + in_definition_scope.then_some(*origin) + } } // Visitor that requires that (almost) all regions in the type visited outlive @@ -459,31 +484,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // } // ``` if let Some(def_id) = def_id.as_local() { - let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.infcx.defining_use_anchor; - let item_kind = &tcx.hir().expect_item(def_id).kind; - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { - span_bug!( - self.value_span, - "weird opaque type: {:#?}, {:#?}", - ty.kind(), - item_kind - ) - }; - let in_definition_scope = match *origin { - // Async `impl Trait` - hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, - // Anonymous `impl Trait` - hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, - // Named `type Foo = impl Bar;` - hir::OpaqueTyOrigin::TyAlias => { - may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) - } - }; - if in_definition_scope { + if let Some(origin) = self.infcx.opaque_type_origin(def_id) { let opaque_type_key = OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; - return self.fold_opaque_ty(ty, opaque_type_key, *origin); + return self.fold_opaque_ty(ty, opaque_type_key, origin); } debug!( diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 816e770f01252..0e27a82b2b13b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -646,6 +646,7 @@ fn test_debugging_options_tracking_hash() { untracked!(borrowck, String::from("other")); untracked!(deduplicate_diagnostics, false); untracked!(dep_tasks, true); + untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(dont_buffer_diagnostics, true); untracked!(dump_dep_graph, true); untracked!(dump_mir, Some(String::from("abc"))); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 27a06943cbc25..c2f6118227a4a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1805,7 +1805,7 @@ declare_lint! { /// /// ### Example /// - /// ``` + /// ```rust /// if let _ = 123 { /// println!("always runs!"); /// } @@ -2431,7 +2431,19 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: formatting may not be suitable for sub-register argument + /// --> src/main.rs:7:19 + /// | + /// 7 | asm!("mov {0}, {0}", in(reg) 0i16); + /// | ^^^ ^^^ ---- for this argument + /// | + /// = note: `#[warn(asm_sub_register)]` on by default + /// = help: use the `x` modifier to have the register formatted as `ax` + /// = help: or use the `r` modifier to keep the default formatting of `rax` + /// ``` /// /// ### Explanation /// @@ -2470,7 +2482,17 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + /// --> src/main.rs:8:14 + /// | + /// 8 | ".att_syntax", + /// | ^^^^^^^^^^^ + /// | + /// = note: `#[warn(bad_asm_style)]` on by default + /// ``` /// /// ### Explanation /// @@ -2788,7 +2810,7 @@ declare_lint! { /// /// ### Example /// - /// ```compile_fail + /// ```rust,compile_fail /// #![feature(staged_api)] /// /// #[derive(Clone)] @@ -3618,7 +3640,17 @@ declare_lint! { /// fn foo() {} /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: duplicated attribute + /// --> src/lib.rs:2:1 + /// | + /// 2 | #[test] + /// | ^^^^^^^ + /// | + /// = note: `#[warn(duplicate_macro_attributes)]` on by default + /// ``` /// /// ### Explanation /// diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 639d2e617c77a..13cd8e4a046b0 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -274,11 +274,6 @@ impl Collector<'_> { span, "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows", ); - } else if !self.tcx.sess.target.options.is_like_msvc { - self.tcx.sess.span_warn( - span, - "`#[link(...)]` with `kind = \"raw-dylib\"` not supported on windows-gnu", - ); } if lib_name.as_str().contains('\0') { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0b9623d1c7d6f..c48d8d689c10e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1161,6 +1161,8 @@ options! { dep_tasks: bool = (false, parse_bool, [UNTRACKED], "print tasks that execute and the color their dep node gets (requires debug build) \ (default: no)"), + dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], + "import library generation tool (windows-gnu only)"), dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ (default: no)"), diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index b7e276b69656f..c351a9f70404a 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -31,9 +31,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: TypeError<'tcx>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_box_deref(err, expr, expected, expr_ty); - self.suggest_compatible_variants(err, expr, expected, expr_ty); self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); + self.suggest_compatible_variants(err, expr, expected, expr_ty); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } @@ -259,23 +258,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_box_deref( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - expr_ty: Ty<'tcx>, - ) { - if expr_ty.is_box() && expr_ty.boxed_ty() == expected { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "try dereferencing the `Box`", - "*".to_string(), - Applicability::MachineApplicable, - ); - } - } - /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( @@ -857,14 +839,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, false, )); - } else if self.infcx.type_is_copy_modulo_regions( - self.param_env, - expected, - sp, - ) { - // For this suggestion to make sense, the type would need to be `Copy`. + } + + // For this suggestion to make sense, the type would need to be `Copy`, + // or we have to be moving out of a `Box<T>` + if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) + || checked_ty.is_box() + { if let Ok(code) = sm.span_to_snippet(expr.span) { - let message = if checked_ty.is_region_ptr() { + let message = if checked_ty.is_box() { + "consider unboxing the value" + } else if checked_ty.is_region_ptr() { "consider dereferencing the borrow" } else { "consider dereferencing the type" diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7e663fab16af5..1cbc2b65f4dbd 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -119,7 +119,6 @@ #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(layout_for_ptr)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![cfg_attr(test, feature(new_uninit))] #![feature(nonnull_slice_from_raw_parts)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3b0e4a31db1c8..dd2f73063566e 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -330,7 +330,7 @@ impl<T> MaybeUninit<T> { /// # Examples /// /// ```no_run - /// #![feature(maybe_uninit_uninit_array, maybe_uninit_extra, maybe_uninit_slice)] + /// #![feature(maybe_uninit_uninit_array, maybe_uninit_slice)] /// /// use std::mem::MaybeUninit; /// @@ -662,7 +662,6 @@ impl<T> MaybeUninit<T> { /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::<u32>::uninit(); @@ -683,7 +682,6 @@ impl<T> MaybeUninit<T> { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninit(); @@ -693,8 +691,8 @@ impl<T> MaybeUninit<T> { /// // We now created two copies of the same vector, leading to a double-free ⚠️ when /// // they both get dropped! /// ``` - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] - #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init_read", issue = "63567")] #[inline(always)] #[track_caller] pub const unsafe fn assume_init_read(&self) -> T { @@ -728,7 +726,7 @@ impl<T> MaybeUninit<T> { /// /// [`assume_init`]: MaybeUninit::assume_init /// [`Vec<T>`]: ../../std/vec/struct.Vec.html - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and // satisfies all invariants of `T`. diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index ec700346ac91d..841c114063dc1 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(const_convert)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init)] +#![feature(const_maybe_uninit_assume_init_read)] #![feature(const_num_from_num)] #![feature(const_ptr_read)] #![feature(const_ptr_write)] @@ -46,7 +47,6 @@ #![feature(slice_take)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_array_assume_init)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(numfmt)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 4ba4e2a528e60..1721e16f3a686 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -297,7 +297,6 @@ #![feature(llvm_asm)] #![feature(log_syntax)] #![feature(map_try_insert)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 56ca7c0392800..d60be193bda2b 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -84,6 +84,39 @@ in documentation. `#![feature(doc_cfg)]` feature gate. For more information, see [its chapter in the Unstable Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg]. +### `doc_auto_cfg`: Automatically generate `#[doc(cfg)]` + +`doc_auto_cfg` is an extension to the `#[doc(cfg)]` feature. With it, you don't need to add +`#[doc(cfg(...)]` anymore unless you want to override the default behaviour. So if we take the +previous source code: + +```rust +#![feature(doc_auto_cfg)] + +/// Token struct that can only be used on Windows. +#[cfg(any(windows, doc))] +pub struct WindowsToken; + +/// Token struct that can only be used on Unix. +#[cfg(any(unix, doc))] +pub struct UnixToken; + +/// Token struct that is only available with the `serde` feature +#[cfg(feature = "serde")] +#[derive(serde::Deserialize)] +pub struct SerdeToken; +``` + +It'll render almost the same, the difference being that `doc` will also be displayed. To fix this, +you can use `doc_cfg_hide`: + +```rust +#![feature(doc_cfg_hide)] +#![doc(cfg_hide(doc))] +``` + +And `doc` won't show up anymore! + [cfg-doc]: ./advanced-features.md [unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html [issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781 diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 2e763dbd8fe9e..d5e5af7bbf8dd 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -240,7 +240,7 @@ pub(super) fn write_shared( } if (*cx.shared).layout.logo.is_empty() { - write_toolchain("rust-logo.png", static_files::RUST_LOGO)?; + write_toolchain("rust-logo.svg", static_files::RUST_LOGO_SVG)?; } if (*cx.shared).layout.favicon.is_empty() { write_toolchain("favicon.svg", static_files::RUST_FAVICON_SVG)?; diff --git a/src/librustdoc/html/static/images/favicon-16x16.png b/src/librustdoc/html/static/images/favicon-16x16.png index 7cfe6c13550d3..ea4b45cae1618 100644 Binary files a/src/librustdoc/html/static/images/favicon-16x16.png and b/src/librustdoc/html/static/images/favicon-16x16.png differ diff --git a/src/librustdoc/html/static/images/favicon-32x32.png b/src/librustdoc/html/static/images/favicon-32x32.png index 5109c1de8bea7..69b8613ce1506 100644 Binary files a/src/librustdoc/html/static/images/favicon-32x32.png and b/src/librustdoc/html/static/images/favicon-32x32.png differ diff --git a/src/librustdoc/html/static/images/rust-logo.png b/src/librustdoc/html/static/images/rust-logo.png deleted file mode 100644 index 74b4bd695045e..0000000000000 Binary files a/src/librustdoc/html/static/images/rust-logo.png and /dev/null differ diff --git a/src/librustdoc/html/static/images/rust-logo.svg b/src/librustdoc/html/static/images/rust-logo.svg new file mode 100644 index 0000000000000..62424d8ffd763 --- /dev/null +++ b/src/librustdoc/html/static/images/rust-logo.svg @@ -0,0 +1,61 @@ +<svg version="1.1" height="106" width="106" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="logo" transform="translate(53, 53)"> + <path id="r" transform="translate(0.5, 0.5)" stroke="black" stroke-width="1" stroke-linejoin="round" d=" + M -9,-15 H 4 C 12,-15 12,-7 4,-7 H -9 Z + M -40,22 H 0 V 11 H -9 V 3 H 1 C 12,3 6,22 15,22 H 40 + V 3 H 34 V 5 C 34,13 25,12 24,7 C 23,2 19,-2 18,-2 C 33,-10 24,-26 12,-26 H -35 + V -15 H -25 V 11 H -40 Z" /> + <g id="gear" mask="url(#holes)"> + <circle r="43" fill="none" stroke="black" stroke-width="9" /> + <g id="cogs"> + <polygon id="cog" stroke="black" stroke-width="3" stroke-linejoin="round" points="46,3 51,0 46,-3" /> + <use xlink:href="#cog" transform="rotate(11.25)" /> + <use xlink:href="#cog" transform="rotate(22.50)" /> + <use xlink:href="#cog" transform="rotate(33.75)" /> + <use xlink:href="#cog" transform="rotate(45.00)" /> + <use xlink:href="#cog" transform="rotate(56.25)" /> + <use xlink:href="#cog" transform="rotate(67.50)" /> + <use xlink:href="#cog" transform="rotate(78.75)" /> + <use xlink:href="#cog" transform="rotate(90.00)" /> + <use xlink:href="#cog" transform="rotate(101.25)" /> + <use xlink:href="#cog" transform="rotate(112.50)" /> + <use xlink:href="#cog" transform="rotate(123.75)" /> + <use xlink:href="#cog" transform="rotate(135.00)" /> + <use xlink:href="#cog" transform="rotate(146.25)" /> + <use xlink:href="#cog" transform="rotate(157.50)" /> + <use xlink:href="#cog" transform="rotate(168.75)" /> + <use xlink:href="#cog" transform="rotate(180.00)" /> + <use xlink:href="#cog" transform="rotate(191.25)" /> + <use xlink:href="#cog" transform="rotate(202.50)" /> + <use xlink:href="#cog" transform="rotate(213.75)" /> + <use xlink:href="#cog" transform="rotate(225.00)" /> + <use xlink:href="#cog" transform="rotate(236.25)" /> + <use xlink:href="#cog" transform="rotate(247.50)" /> + <use xlink:href="#cog" transform="rotate(258.75)" /> + <use xlink:href="#cog" transform="rotate(270.00)" /> + <use xlink:href="#cog" transform="rotate(281.25)" /> + <use xlink:href="#cog" transform="rotate(292.50)" /> + <use xlink:href="#cog" transform="rotate(303.75)" /> + <use xlink:href="#cog" transform="rotate(315.00)" /> + <use xlink:href="#cog" transform="rotate(326.25)" /> + <use xlink:href="#cog" transform="rotate(337.50)" /> + <use xlink:href="#cog" transform="rotate(348.75)" /> + </g> + <g id="mounts"> + <polygon id="mount" stroke="black" stroke-width="6" stroke-linejoin="round" points="-7,-42 0,-35 7,-42" /> + <use xlink:href="#mount" transform="rotate(72)" /> + <use xlink:href="#mount" transform="rotate(144)" /> + <use xlink:href="#mount" transform="rotate(216)" /> + <use xlink:href="#mount" transform="rotate(288)" /> + </g> + </g> + <mask id="holes"> + <rect x="-60" y="-60" width="120" height="120" fill="white"/> + <circle id="hole" cy="-40" r="3" /> + <use xlink:href="#hole" transform="rotate(72)" /> + <use xlink:href="#hole" transform="rotate(144)" /> + <use xlink:href="#hole" transform="rotate(216)" /> + <use xlink:href="#hole" transform="rotate(288)" /> + </mask> +</g> +</svg> diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 56c5399d074b6..cd369a93d8283 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -67,8 +67,9 @@ crate static LICENSE_APACHE: &[u8] = include_bytes!("static/LICENSE-APACHE.txt") /// The contents of `LICENSE-MIT.txt`, the text of the MIT License. crate static LICENSE_MIT: &[u8] = include_bytes!("static/LICENSE-MIT.txt"); -/// The contents of `rust-logo.png`, the default icon of the documentation. -crate static RUST_LOGO: &[u8] = include_bytes!("static/images/rust-logo.png"); +/// The contents of `rust-logo.svg`, the default icon of the documentation. +crate static RUST_LOGO_SVG: &[u8] = include_bytes!("static/images/rust-logo.svg"); + /// The default documentation favicons (SVG and PNG fallbacks) crate static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/images/favicon.svg"); crate static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/images/favicon-16x16.png"); diff --git a/src/librustdoc/templates/page.html b/src/librustdoc/templates/page.html index 673260ac6d09d..02808754b539b 100644 --- a/src/librustdoc/templates/page.html +++ b/src/librustdoc/templates/page.html @@ -79,7 +79,7 @@ {%- if !layout.logo.is_empty() %} <img src="{{layout.logo}}" alt="logo"> {#- -#} {%- else -%} - <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.png" alt="logo"> {#- -#} + <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#} {%- endif -%} </div> </a> {#- -#} @@ -92,7 +92,7 @@ {%- if !layout.logo.is_empty() %} <img src="{{layout.logo}}" alt="logo"> {#- -#} {%- else -%} - <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.png" alt="logo"> {#- -#} + <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#} {%- endif -%} </a> {#- -#} <nav class="sub"> {#- -#} diff --git a/src/librustdoc/templates/print_item.html b/src/librustdoc/templates/print_item.html index c98c6db424a19..459b01a9960d2 100644 --- a/src/librustdoc/templates/print_item.html +++ b/src/librustdoc/templates/print_item.html @@ -16,12 +16,12 @@ <h1 class="fqn"> {#- -#} </h1> {#- -#} <span class="out-of-band"> {#- -#} {% if !stability_since_raw.is_empty() %} - {{- stability_since_raw|safe -}} · + {{- stability_since_raw|safe -}} · {# -#} {% endif %} {%- match src_href -%} {%- when Some with (href) -%} - <a class="srclink" href="{{href|safe}}" title="goto source code">source</a> - {%- else -%} · + <a class="srclink" href="{{href|safe}}" title="goto source code">source</a> · {# -#} + {%- else -%} {%- endmatch -%} <a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs"> {#- -#} [<span class="inner">−</span>] {#- -#} diff --git a/src/test/run-make/raw-dylib-c/Makefile b/src/test/run-make/raw-dylib-c/Makefile index 26ab4d34764d1..166305672e6f2 100644 --- a/src/test/run-make/raw-dylib-c/Makefile +++ b/src/test/run-make/raw-dylib-c/Makefile @@ -1,14 +1,19 @@ # Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc -# only-windows-msvc +# only-windows -include ../../run-make-fulldeps/tools.mk all: $(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c) $(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c) +ifdef IS_MSVC $(CC) "$(TMPDIR)"/extern_1.obj -link -dll -out:"$(TMPDIR)"/extern_1.dll $(CC) "$(TMPDIR)"/extern_2.obj -link -dll -out:"$(TMPDIR)"/extern_2.dll +else + $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll + $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll +endif $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt diff --git a/src/test/run-make/raw-dylib-link-ordinal/Makefile b/src/test/run-make/raw-dylib-link-ordinal/Makefile index 04b257d063204..0e84a749b0578 100644 --- a/src/test/run-make/raw-dylib-link-ordinal/Makefile +++ b/src/test/run-make/raw-dylib-link-ordinal/Makefile @@ -1,12 +1,16 @@ # Test the behavior of #[link(.., kind = "raw-dylib")] and #[link_ordinal] on windows-msvc -# only-windows-msvc +# only-windows -include ../../run-make-fulldeps/tools.mk all: $(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c) +ifdef IS_MSVC $(CC) "$(TMPDIR)"/exporter.obj exporter.def -link -dll -out:"$(TMPDIR)"/exporter.dll +else + $(CC) "$(TMPDIR)"/exporter.obj exporter.def -shared -o "$(TMPDIR)"/exporter.dll +endif $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile b/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile new file mode 100644 index 0000000000000..69f62669d6291 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile @@ -0,0 +1,23 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")], #[link_ordinal], and alternative calling conventions on i686 windows. + +# only-x86 +# only-windows + +-include ../../run-make-fulldeps/tools.mk + +all: + $(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/exporter.obj exporter-msvc.def -link -dll -out:"$(TMPDIR)"/exporter.dll +else + $(CC) "$(TMPDIR)"/exporter.obj exporter-gnu.def -shared -o "$(TMPDIR)"/exporter.dll +endif + $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + "$(TMPDIR)"/driver > "$(TMPDIR)"/actual_output.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/actual_output.txt expected_output.txt +else + $(DIFF) expected_output.txt "$(TMPDIR)"/actual_output.txt +endif diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs b/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs new file mode 100644 index 0000000000000..4059ede11fc96 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs @@ -0,0 +1,5 @@ +extern crate raw_dylib_test; + +fn main() { + raw_dylib_test::library_function(); +} diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt b/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt new file mode 100644 index 0000000000000..20157763745f8 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt @@ -0,0 +1,2 @@ +exported_function_stdcall(6) +exported_function_fastcall(125) diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def new file mode 100644 index 0000000000000..8d28d714b7e64 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def @@ -0,0 +1,4 @@ +LIBRARY exporter +EXPORTS + exported_function_stdcall@4 @15 NONAME + @exported_function_fastcall@4 @18 NONAME \ No newline at end of file diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def new file mode 100644 index 0000000000000..5a4c79a58edfd --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def @@ -0,0 +1,4 @@ +LIBRARY exporter +EXPORTS + _exported_function_stdcall@4 @15 NONAME + @exported_function_fastcall@4 @18 NONAME \ No newline at end of file diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c new file mode 100644 index 0000000000000..1fb45bf010ff2 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c @@ -0,0 +1,11 @@ +#include <stdio.h> + +void __stdcall exported_function_stdcall(int i) { + printf("exported_function_stdcall(%d)\n", i); + fflush(stdout); +} + +void __fastcall exported_function_fastcall(int i) { + printf("exported_function_fastcall(%d)\n", i); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs b/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs new file mode 100644 index 0000000000000..07dd3d7be9b90 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs @@ -0,0 +1,20 @@ +#![feature(raw_dylib)] + +#[link(name = "exporter", kind = "raw-dylib")] +extern "stdcall" { + #[link_ordinal(15)] + fn imported_function_stdcall(i: i32); +} + +#[link(name = "exporter", kind = "raw-dylib")] +extern "fastcall" { + #[link_ordinal(18)] + fn imported_function_fastcall(i: i32); +} + +pub fn library_function() { + unsafe { + imported_function_stdcall(6); + imported_function_fastcall(125); + } +} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs deleted file mode 100644 index 33f9c5393135f..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs +++ /dev/null @@ -1,8 +0,0 @@ -// gate-test-raw_dylib -// only-windows-gnu -#[link(name = "foo", kind = "raw-dylib")] -//~^ ERROR: kind="raw-dylib" is unstable -//~| WARNING: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu -extern "C" {} - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr deleted file mode 100644 index 14dfadf4126f3..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr +++ /dev/null @@ -1,18 +0,0 @@ -warning: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu - --> $DIR/feature-gate-raw-dylib-windows-gnu.rs:3:1 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0658]: kind="raw-dylib" is unstable - --> $DIR/feature-gate-raw-dylib-windows-gnu.rs:3:1 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information - = help: add `#![feature(raw_dylib)]` to the crate attributes to enable - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib.rs similarity index 71% rename from src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs rename to src/test/ui/feature-gates/feature-gate-raw-dylib.rs index 49de24ea9ab42..995d9ced4801a 100644 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs +++ b/src/test/ui/feature-gates/feature-gate-raw-dylib.rs @@ -1,5 +1,4 @@ -// gate-test-raw_dylib -// only-windows-msvc +// only-windows #[link(name = "foo", kind = "raw-dylib")] //~^ ERROR: kind="raw-dylib" is unstable extern "C" {} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib.stderr similarity index 88% rename from src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr rename to src/test/ui/feature-gates/feature-gate-raw-dylib.stderr index 1198808081213..bb64af38b2cb9 100644 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr +++ b/src/test/ui/feature-gates/feature-gate-raw-dylib.stderr @@ -1,5 +1,5 @@ error[E0658]: kind="raw-dylib" is unstable - --> $DIR/feature-gate-raw-dylib-windows-msvc.rs:3:1 + --> $DIR/feature-gate-raw-dylib.rs:2:1 | LL | #[link(name = "foo", kind = "raw-dylib")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/infinite/infinite-autoderef.stderr b/src/test/ui/infinite/infinite-autoderef.stderr index 03e4718f5dfef..2d29f0592e14b 100644 --- a/src/test/ui/infinite/infinite-autoderef.stderr +++ b/src/test/ui/infinite/infinite-autoderef.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/infinite-autoderef.rs:20:13 | LL | x = Box::new(x); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | x = *Box::new(x); + | + error[E0055]: reached the recursion limit while auto-dereferencing `Foo` --> $DIR/infinite-autoderef.rs:25:5 diff --git a/src/test/ui/occurs-check-2.stderr b/src/test/ui/occurs-check-2.stderr index dcbfc81b4d519..b68c3fa5bcc9c 100644 --- a/src/test/ui/occurs-check-2.stderr +++ b/src/test/ui/occurs-check-2.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/occurs-check-2.rs:7:9 | LL | f = Box::new(g); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(g); + | + error: aborting due to previous error diff --git a/src/test/ui/occurs-check.stderr b/src/test/ui/occurs-check.stderr index 3e1ef2e719ad5..fdbbdc3abb377 100644 --- a/src/test/ui/occurs-check.stderr +++ b/src/test/ui/occurs-check.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/occurs-check.rs:5:9 | LL | f = Box::new(f); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(f); + | + error: aborting due to previous error diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs index 0ab994ecd45be..2a15b1d799f84 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs @@ -1,4 +1,4 @@ -// only-windows-msvc +// only-windows #![feature(raw_dylib)] //~^ WARN the feature `raw_dylib` is incomplete diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs index d02bebc9d61d2..13c9aa01e34ae 100644 --- a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs +++ b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs @@ -1,4 +1,5 @@ -// only-i686-pc-windows-msvc +// only-x86 +// only-windows // compile-flags: --crate-type lib --emit link #![allow(clashing_extern_declarations)] #![feature(raw_dylib)] diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr index a9cfd6b23f9f8..93ca8f4d8d448 100644 --- a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr @@ -1,5 +1,5 @@ warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/multiple-declarations.rs:4:12 + --> $DIR/multiple-declarations.rs:5:12 | LL | #![feature(raw_dylib)] | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #![feature(raw_dylib)] = note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information error: multiple declarations of external function `f` from library `foo.dll` have different calling conventions - --> $DIR/multiple-declarations.rs:14:9 + --> $DIR/multiple-declarations.rs:15:9 | LL | fn f(x: i32); | ^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs deleted file mode 100644 index e9690f03f45c9..0000000000000 --- a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs +++ /dev/null @@ -1,8 +0,0 @@ -// only-windows-gnu -// check-pass -// compile-flags: --crate-type lib -#![feature(raw_dylib)] -//~^ WARNING: the feature `raw_dylib` is incomplete -#[link(name = "foo", kind = "raw-dylib")] -//~^ WARNING: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu -extern "C" {} diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr deleted file mode 100644 index 6e24112b3c3e5..0000000000000 --- a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/raw-dylib-msvc-only.rs:4:12 - | -LL | #![feature(raw_dylib)] - | ^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information - -warning: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu - --> $DIR/raw-dylib-msvc-only.rs:6:1 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: 2 warnings emitted - diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs index e5a5ac2eb2bf1..dc647fd63f527 100644 --- a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs +++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs @@ -1,4 +1,5 @@ -// only-x86_64-pc-windows-msvc +// only-x86_64 +// only-windows // compile-flags: --crate-type lib --emit link #![allow(incomplete_features)] #![feature(raw_dylib)] diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr index fc9008128ae43..d8a2a6af9c19e 100644 --- a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr @@ -1,5 +1,5 @@ error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture - --> $DIR/unsupported-abi.rs:7:5 + --> $DIR/unsupported-abi.rs:8:5 | LL | fn f(x: i32); | ^^^^^^^^^^^^^ diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 74caae8645c1d..5a964c5d5cc66 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -38,9 +38,12 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:17:9 | LL | f = Box::new(f); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(f); + | + error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:21:9 diff --git a/src/test/ui/suggestions/boxed-variant-field.rs b/src/test/ui/suggestions/boxed-variant-field.rs index 9b9e70a675fb1..e79be2f6127c1 100644 --- a/src/test/ui/suggestions/boxed-variant-field.rs +++ b/src/test/ui/suggestions/boxed-variant-field.rs @@ -8,7 +8,7 @@ fn foo(x: Ty) -> Ty { Ty::Unit => Ty::Unit, Ty::List(elem) => foo(elem), //~^ ERROR mismatched types - //~| HELP try dereferencing the `Box` + //~| HELP consider unboxing the value //~| HELP try wrapping } } diff --git a/src/test/ui/suggestions/boxed-variant-field.stderr b/src/test/ui/suggestions/boxed-variant-field.stderr index e865b993a4c17..9a31dc89197e3 100644 --- a/src/test/ui/suggestions/boxed-variant-field.stderr +++ b/src/test/ui/suggestions/boxed-variant-field.stderr @@ -6,7 +6,7 @@ LL | Ty::List(elem) => foo(elem), | = note: expected enum `Ty` found struct `Box<Ty>` -help: try dereferencing the `Box` +help: consider unboxing the value | LL | Ty::List(elem) => foo(*elem), | + diff --git a/src/test/ui/terr-sorts.stderr b/src/test/ui/terr-sorts.stderr index 65d90678040d5..34d4d9eaded8a 100644 --- a/src/test/ui/terr-sorts.stderr +++ b/src/test/ui/terr-sorts.stderr @@ -6,7 +6,7 @@ LL | want_foo(b); | = note: expected struct `Foo` found struct `Box<Foo>` -help: try dereferencing the `Box` +help: consider unboxing the value | LL | want_foo(*b); | + diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml index 688473f2f9bfc..f3dd9275a42be 100644 --- a/src/tools/clippy/.cargo/config.toml +++ b/src/tools/clippy/.cargo/config.toml @@ -2,9 +2,12 @@ uitest = "test --test compile-test" dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " -collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" +collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored" [build] # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"] target-dir = "target" + +[unstable] +binary-dep-depinfo = true diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 3d8c39408a924..116ae031bb719 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -49,17 +49,17 @@ jobs: echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build - run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo build --features deny-warnings,internal - name: Test - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_utils - name: Test rustc_tools_util @@ -70,14 +70,6 @@ jobs: run: cargo test --features deny-warnings working-directory: clippy_dev - - name: Test cargo-clippy - run: ../target/debug/cargo-clippy - working-directory: clippy_workspace_tests - - - name: Test cargo-clippy --fix - run: ../target/debug/cargo-clippy clippy --fix - working-directory: clippy_workspace_tests - - name: Test clippy-driver run: bash .github/driver.sh env: diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 8b644aa28176b..989667037c1cb 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -112,17 +112,22 @@ jobs: echo "$SYSROOT/bin" >> $GITHUB_PATH - name: Build - run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo build --features deny-warnings,internal - name: Test - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + if: runner.os == 'Linux' + run: cargo test --features deny-warnings,internal + + - name: Test + if: runner.os != 'Linux' + run: cargo test --features deny-warnings,internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_utils - name: Test rustc_tools_util @@ -133,14 +138,6 @@ jobs: run: cargo test --features deny-warnings working-directory: clippy_dev - - name: Test cargo-clippy - run: ../target/debug/cargo-clippy - working-directory: clippy_workspace_tests - - - name: Test cargo-clippy --fix - run: ../target/debug/cargo-clippy clippy --fix - working-directory: clippy_workspace_tests - - name: Test clippy-driver run: bash .github/driver.sh env: diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore index e82a0ec4765bb..3e50c45a9b63e 100644 --- a/src/tools/clippy/.gitignore +++ b/src/tools/clippy/.gitignore @@ -19,7 +19,6 @@ out /target /clippy_lints/target /clippy_utils/target -/clippy_workspace_tests/target /clippy_dev/target /lintcheck/target /rustc_tools_util/target diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 27bac4718b6c3..8f4da9a382792 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -2887,6 +2887,7 @@ Released 2018-09-13 [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection @@ -3070,6 +3071,7 @@ Released 2018-09-13 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten @@ -3253,6 +3255,7 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 8661a86775887..e445889a58f77 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.59" +version = "0.1.60" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -47,7 +47,9 @@ itertools = "0.10" quote = "1.0" serde = { version = "1.0", features = ["derive"] } syn = { version = "1.0", features = ["full"] } +futures = "0.3" parking_lot = "0.11.2" +tokio = { version = "1", features = ["io-util"] } [build-dependencies] rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } @@ -55,8 +57,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } [features] deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] -internal-lints = ["clippy_lints/internal-lints"] -metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"] +internal = ["clippy_lints/internal"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 1bbd89e7822e8..f001a42d917d3 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. @@ -37,8 +37,8 @@ Table of contents: ## Usage -Below are instructions on how to use Clippy as a subcommand, compiled from source -or in Travis CI. +Below are instructions on how to use Clippy as a cargo subcommand, +in projects that do not use cargo, or in Travis CI. ### As a cargo subcommand (`cargo clippy`) @@ -98,22 +98,18 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio cargo clippy -p example -- --no-deps ``` -### As a rustc replacement (`clippy-driver`) +### Using `clippy-driver` -Clippy can also be used in projects that do not use cargo. To do so, you will need to replace -your `rustc` compilation commands with `clippy-driver`. For example, if your project runs: - -```terminal -rustc --edition 2018 -Cpanic=abort foo.rs -``` - -Then, to enable Clippy, you will need to call: +Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver` +with the same arguments you use for `rustc`. For example: ```terminal clippy-driver --edition 2018 -Cpanic=abort foo.rs ``` -Note that `rustc` will still run, i.e. it will still emit the output files it normally does. +Note that `clippy-driver` is designed for running Clippy only and should not be used as a general +replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected, +for example. ### Travis CI diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 9ceadee58ea59..d513a229b7e38 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use shell_escape::escape; use std::ffi::{OsStr, OsString}; use std::path::Path; -use std::process::{self, Command}; +use std::process::{self, Command, Stdio}; use std::{fs, io}; use walkdir::WalkDir; @@ -31,6 +31,7 @@ impl From<walkdir::Error> for CliError { struct FmtContext { check: bool, verbose: bool, + rustfmt_path: String, } // the "main" function of cargo dev fmt @@ -102,7 +103,23 @@ Please revert the changes to Cargo.tomls first." } } - let context = FmtContext { check, verbose }; + let output = Command::new("rustup") + .args(["which", "rustfmt"]) + .stderr(Stdio::inherit()) + .output() + .expect("error running `rustup which rustfmt`"); + if !output.status.success() { + eprintln!("`rustup which rustfmt` did not execute successfully"); + process::exit(1); + } + let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path"); + rustfmt_path.truncate(rustfmt_path.trim_end().len()); + + let context = FmtContext { + check, + verbose, + rustfmt_path, + }; let result = try_run(&context); let code = match result { Ok(true) => 0, @@ -141,8 +158,12 @@ fn exec( println!("{}", format_command(&program, &dir, args)); } - let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?; - let output = child.wait_with_output()?; + let output = Command::new(&program) + .env("RUSTFMT", &context.rustfmt_path) + .current_dir(&dir) + .args(args.iter()) + .output() + .unwrap(); let success = output.status.success(); if !context.check && !success { @@ -159,7 +180,6 @@ fn exec( fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> { let mut args = vec!["fmt", "--all"]; if context.check { - args.push("--"); args.push("--check"); } let success = exec(context, "cargo", path, &args)?; @@ -200,7 +220,7 @@ fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Resul } args.extend(paths); - let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?; + let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?; Ok(success) } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 8dd073ef405a1..d368ef1f46a2a 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -321,7 +321,7 @@ fn gen_register_lint_list<'a>( for (is_public, module_name, lint_name) in details { if !is_public { - output.push_str(" #[cfg(feature = \"internal-lints\")]\n"); + output.push_str(" #[cfg(feature = \"internal\")]\n"); } output.push_str(&format!(" {}::{},\n", module_name, lint_name)); } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 7d2a3e4f639c3..2053ca64ba23d 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.59" +version = "0.1.60" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -30,8 +30,7 @@ url = { version = "2.2", features = ["serde"] } [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default -internal-lints = ["clippy_utils/internal-lints"] -metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"] +internal = ["clippy_utils/internal", "serde_json"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index b7f414742f156..c82837746bd5d 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -1,12 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::higher; -use clippy_utils::source::snippet_opt; -use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks}; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, UnOp}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let lint_true = |is_debug: bool| { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - e.span, - if is_debug { - "`debug_assert!(true)` will be optimized out by the compiler" - } else { - "`assert!(true)` will be optimized out by the compiler" - }, - None, - "remove it", - ); + let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, }; - let lint_false_without_message = || { + let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return }; + let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return }; + if val { span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - "`assert!(false)` should probably be replaced", + macro_call.span, + &format!( + "`{}!(true)` will be optimized out by the compiler", + cx.tcx.item_name(macro_call.def_id) + ), None, - "use `panic!()` or `unreachable!()`", + "remove it", ); - }; - let lint_false_with_message = |panic_message: String| { + } else if !is_debug { + let (assert_arg, panic_arg) = match panic_expn { + PanicExpn::Empty => ("", ""), + _ => (", ..", ".."), + }; span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - &format!("`assert!(false, {})` should probably be replaced", panic_message), + macro_call.span, + &format!("`assert!(false{})` should probably be replaced", assert_arg), None, - &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message), + &format!("use `panic!({})` or `unreachable!({0})`", panic_arg), ); - }; - - if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") { - if debug_assert_span.from_expansion() { - return; - } - if_chain! { - if let ExprKind::Unary(_, lit) = e.kind; - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit); - if is_true; - then { - lint_true(true); - } - }; - } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") { - if assert_span.from_expansion() { - return; - } - if let Some(assert_match) = match_assert_with_message(cx, e) { - match assert_match { - // matched assert but not message - AssertKind::WithoutMessage(false) => lint_false_without_message(), - AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false), - AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message), - }; - } - } - } -} - -/// Result of calling `match_assert_with_message`. -enum AssertKind { - WithMessage(String, bool), - WithoutMessage(bool), -} - -/// Check if the expression matches -/// -/// ```rust,ignore -/// if !c { -/// { -/// ::std::rt::begin_panic(message, _) -/// } -/// } -/// ``` -/// -/// where `message` is any expression and `c` is a constant bool. -fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> { - if_chain! { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); - if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; - // bind the first argument of the `assert!` macro - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); - let begin_panic_call = peel_blocks(then); - // function call - if let Some(arg) = match_panic_call(cx, begin_panic_call); - // bind the second argument of the `assert!` macro if it exists - if let panic_message = snippet_opt(cx, arg.span); - // second argument of begin_panic is irrelevant - // as is the second match arm - then { - // an empty message occurs when it was generated by the macro - // (and not passed by the user) - return panic_message - .filter(|msg| !msg.is_empty()) - .map(|msg| AssertKind::WithMessage(msg, is_true)) - .or(Some(AssertKind::WithoutMessage(is_true))); } } - None } diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 0629674307ba7..a58d12ddd6b43 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -1,9 +1,10 @@ //! checks for attributes use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv}; +use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; @@ -443,20 +444,15 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ } fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { + if macro_backtrace(expr.span).last().map_or(false, |macro_call| { + is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable + }) { + return false; + } match &expr.kind { ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), ExprKind::Ret(None) | ExprKind::Break(_, None) => false, - ExprKind::Call(path_expr, _) => { - if let ExprKind::Path(qpath) = &path_expr.kind { - typeck_results - .qpath_res(qpath, path_expr.hir_id) - .opt_def_id() - .map_or(true, |fun_id| !match_panic_def_id(cx, fun_id)) - } else { - true - } - }, _ => true, } } diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index d0b8c52a36a92..c50e214be288d 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit}; @@ -41,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool { ) && !e.span.from_expansion() } -fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(e); cx.tcx @@ -66,44 +67,40 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let macros = ["assert_eq", "debug_assert_eq"]; - let inverted_macros = ["assert_ne", "debug_assert_ne"]; - - for mac in macros.iter().chain(inverted_macros.iter()) { - if let Some(span) = is_direct_expn_of(expr.span, mac) { - if let Some(args) = higher::extract_assert_macro_args(expr) { - if let [a, b, ..] = args[..] { - let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b)); - - if nb_bool_args != 1 { - // If there are two boolean arguments, we definitely don't understand - // what's going on, so better leave things as is... - // - // Or there is simply no boolean and then we can leave things as is! - return; - } - - if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { - // At this point the expression which is not a boolean - // literal does not implement Not trait with a bool output, - // so we cannot suggest to rewrite our code - return; - } + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let macro_name = cx.tcx.item_name(macro_call.def_id); + if !matches!( + macro_name.as_str(), + "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne" + ) { + return; + } + let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !(is_bool_lit(a) ^ is_bool_lit(b)) { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } - let non_eq_mac = &mac[..mac.len() - 3]; - span_lint_and_sugg( - cx, - BOOL_ASSERT_COMPARISON, - span, - &format!("used `{}!` with a literal bool", mac), - "replace it with", - format!("{}!(..)", non_eq_mac), - Applicability::MaybeIncorrect, - ); - return; - } - } - } + if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { + // At this point the expression which is not a boolean + // literal does not implement Not trait with a bool output, + // so we cannot suggest to rewrite our code + return; } + + let macro_name = macro_name.as_str(); + let non_eq_mac = ¯o_name[..macro_name.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + macro_call.span, + &format!("used `{}!` with a literal bool", macro_name), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs new file mode 100644 index 0000000000000..b8f5217af2b7d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs @@ -0,0 +1,97 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_no_std_crate; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `&expr as *const T` or + /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or + /// `ptr::addr_of_mut` instead. + /// + /// ### Why is this bad? + /// This would improve readability and avoid creating a reference + /// that points to an uninitialized value or unaligned place. + /// Read the `ptr::addr_of` docs for more information. + /// + /// ### Example + /// ```rust + /// let val = 1; + /// let p = &val as *const i32; + /// + /// let mut val_mut = 1; + /// let p_mut = &mut val_mut as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let val = 1; + /// let p = std::ptr::addr_of!(val); + /// + /// let mut val_mut = 1; + /// let p_mut = std::ptr::addr_of_mut!(val_mut); + /// ``` + #[clippy::version = "1.60.0"] + pub BORROW_AS_PTR, + pedantic, + "borrowing just to cast to a raw pointer" +} + +impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]); + +pub struct BorrowAsPtr { + msrv: Option<RustcVersion>, +} + +impl BorrowAsPtr { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(left_expr, ty) = &expr.kind; + if let TyKind::Ptr(_) = ty.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind; + + then { + let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; + let macro_name = match mutability { + Mutability::Not => "addr_of", + Mutability::Mut => "addr_of_mut", + }; + + span_lint_and_sugg( + cx, + BORROW_AS_PTR, + expr.span, + "borrow as raw pointer", + "try", + format!( + "{}::ptr::{}!({})", + core_or_std, + macro_name, + snippet_opt(cx, e.span).unwrap() + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index 3f286dd9e2fca..e8f39cd37090d 100644 --- a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -67,7 +67,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & None } -impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { +impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index 248b35b024ee1..b9de5510455b9 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::sym; use super::CAST_PTR_ALIGNMENT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs index d9bf1ea58b97b..15f2f81f4079e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use super::CAST_REF_TO_MUT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; if let ExprKind::Cast(e, t) = &e.kind; diff --git a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs index 099a0de881ff0..7cc406018dbe0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy}; use super::CHAR_LIT_AS_U8; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Cast(e, _) = &expr.kind; if let ExprKind::Lit(l) = &e.kind; diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index 3132d3a5cf097..fb04f93fbcf97 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -12,7 +12,7 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) { if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { return; } diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index d07bc23235b0f..73ce656ad1514 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -316,7 +316,7 @@ struct BlockEqual { /// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to /// abort any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> { +fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; @@ -385,11 +385,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option< }) } -fn check_for_warn_of_moved_symbol( - cx: &LateContext<'tcx>, - symbols: &FxHashSet<Symbol>, - if_expr: &'tcx Expr<'_>, -) -> bool { +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool { get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); @@ -419,13 +415,13 @@ fn check_for_warn_of_moved_symbol( } fn emit_branches_sharing_code_lint( - cx: &LateContext<'tcx>, + cx: &LateContext<'_>, start_stmts: usize, end_stmts: usize, lint_end: bool, warn_about_moved_symbol: bool, - blocks: &[&Block<'tcx>], - if_expr: &'tcx Expr<'_>, + blocks: &[&Block<'_>], + if_expr: &Expr<'_>, ) { if start_stmts == 0 && !lint_end { return; diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index 6422f5aabe5e2..3070588483c21 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -77,7 +77,7 @@ pub struct Default { impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); -impl LateLintPass<'_> for Default { +impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); @@ -110,7 +110,7 @@ impl LateLintPass<'_> for Default { } #[allow(clippy::too_many_lines)] - fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding let stmts_head = match block.stmts { diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 15215ac15cdb9..66b5f49817d8d 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -54,7 +54,7 @@ declare_clippy_lint! { declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); -impl LateLintPass<'_> for DefaultNumericFallback { +impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let mut visitor = NumericFallbackVisitor::new(cx); visitor.visit_body(body); diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index fa2b348591be4..bf077a212fd0f 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } } -fn try_parse_ref_op( +fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -387,7 +387,7 @@ fn try_parse_ref_op( // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. -fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { +fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool { match (result_ty.kind(), arg_ty.kind()) { (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), @@ -457,7 +457,7 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, } /// Adjustments are sometimes made in the parent block rather than the expression itself. -fn find_adjustments( +fn find_adjustments<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -499,7 +499,7 @@ fn find_adjustments( } #[allow(clippy::needless_pass_by_value)] -fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { +fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { match state { State::DerefMethod { ty_changed_count, @@ -568,7 +568,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat } impl Dereferencing { - fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) { + fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 6d4065907fb45..73c00d97020be 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -11,6 +11,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured methods and functions in clippy.toml /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// methods are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some methods are undesirable in certain contexts, and it's beneficial to /// lint for them as needed. @@ -49,14 +52,14 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, - nursery, + style, "use of a disallowed method call" } #[derive(Clone, Debug)] pub struct DisallowedMethods { conf_disallowed: Vec<conf::DisallowedMethod>, - disallowed: DefIdMap<Option<String>>, + disallowed: DefIdMap<usize>, } impl DisallowedMethods { @@ -72,17 +75,10 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { - let (path, reason) = match conf { - conf::DisallowedMethod::Simple(path) => (path, None), - conf::DisallowedMethod::WithReason { path, reason } => ( - path, - reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), - ), - }; - let segs: Vec<_> = path.split("::").collect(); + for (index, conf) in self.conf_disallowed.iter().enumerate() { + let segs: Vec<_> = conf.path().split("::").collect(); if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) { - self.disallowed.insert(id, reason); + self.disallowed.insert(id, index); } } } @@ -92,15 +88,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { Some(def_id) => def_id, None => return, }; - let reason = match self.disallowed.get(&def_id) { - Some(reason) => reason, + let conf = match self.disallowed.get(&def_id) { + Some(&index) => &self.conf_disallowed[index], None => return, }; - let func_path = cx.tcx.def_path_str(def_id); - let msg = format!("use of a disallowed method `{}`", func_path); + let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { - if let Some(reason) = reason { - diag.note(reason); + if let conf::DisallowedMethod::WithReason { + reason: Some(reason), .. + } = conf + { + diag.note(&format!("{} (from clippy.toml)", reason)); } }); } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index eaed40327136f..ea4b49b46fe9f 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -14,6 +14,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured types in clippy.toml. /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// types are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some types are undesirable in certain contexts. /// @@ -44,7 +47,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, - nursery, + style, "use of disallowed types" } #[derive(Clone, Debug)] diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 7c2717733578b..cb7d5ac73941a 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -1,8 +1,9 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::{first_line_of_span, snippet_with_applicability}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; +use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; @@ -13,7 +14,7 @@ use rustc_errors::emitter::EmitterWriter; use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{AnonConst, Expr, ExprKind, QPath}; +use rustc_hir::{AnonConst, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { return; } - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.cx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - if !is_expn_of_debug_assertions(expr.span); - then { - self.panic_span = Some(expr.span); + if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { + if is_panic(self.cx, macro_call.def_id) + || matches!( + self.cx.tcx.item_name(macro_call.def_id).as_str(), + "assert" | "assert_eq" | "assert_ne" | "todo" + ) + { + self.panic_span = Some(macro_call.span); } } - // check for `assert_eq` or `assert_ne` - if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() { - self.panic_span = Some(expr.span); - } - // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); @@ -844,8 +838,3 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } - -fn is_expn_of_debug_assertions(span: Span) -> bool { - const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some()) -} diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 3d92eb16870e3..3ce239273e252 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -233,7 +233,7 @@ struct ContainsExpr<'tcx> { key: &'tcx Expr<'tcx>, call_ctxt: SyntaxContext, } -fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { +fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { let mut negated = false; let expr = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::Unary(UnOp::Not, e) => { @@ -280,7 +280,7 @@ struct InsertExpr<'tcx> { key: &'tcx Expr<'tcx>, value: &'tcx Expr<'tcx>, } -fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { +fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) { @@ -301,7 +301,7 @@ enum Edit<'tcx> { /// An insertion into the map. Insertion(Insertion<'tcx>), } -impl Edit<'tcx> { +impl<'tcx> Edit<'tcx> { fn as_insertion(self) -> Option<Insertion<'tcx>> { if let Self::Insertion(i) = self { Some(i) } else { None } } @@ -532,7 +532,7 @@ struct InsertSearchResults<'tcx> { allow_insert_closure: bool, is_single_insert: bool, } -impl InsertSearchResults<'tcx> { +impl<'tcx> InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option<Insertion<'tcx>> { self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } @@ -633,7 +633,7 @@ impl InsertSearchResults<'tcx> { } } -fn find_insert_calls( +fn find_insert_calls<'tcx>( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs index 1012346052733..df75b815436b8 100644 --- a/src/tools/clippy/clippy_lints/src/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/eq_op.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function}; +use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -68,32 +69,26 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Block(block, _) = e.kind { - for stmt in block.stmts { - for amn in &ASSERT_MACRO_NAMES { - if_chain! { - if is_expn_of(stmt.span, amn).is_some(); - if let StmtKind::Semi(matchexpr) = stmt.kind; - if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); - if macro_args.len() == 2; - let (lhs, rhs) = (macro_args[0], macro_args[1]); - if eq_expr_value(cx, lhs, rhs); - if !is_in_test_function(cx.tcx, e.hir_id); - then { - span_lint( - cx, - EQ_OP, - lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", amn), - ); - } - } - } + if_chain! { + if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { + let name = cx.tcx.item_name(macro_call.def_id); + matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne") + .then(|| (macro_call, name)) + }); + if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn); + if eq_expr_value(cx, lhs, rhs); + if macro_call.is_local(); + if !is_in_test_function(cx.tcx, e.hir_id); + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", macro_name), + ); } } if let ExprKind::Binary(op, left, right) = e.kind { diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 06d128f5527b5..cf47e581ccb48 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { } } -fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { +fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { if let Some(def_id) = cx.tcx.lang_items().eq_trait() { implements_trait(cx, ty, def_id, &[other.into()]) } else { diff --git a/src/tools/clippy/clippy_lints/src/erasing_op.rs b/src/tools/clippy/clippy_lints/src/erasing_op.rs index d49cec26be5f0..c1a84973c4211 100644 --- a/src/tools/clippy/clippy_lints/src/erasing_op.rs +++ b/src/tools/clippy/clippy_lints/src/erasing_op.rs @@ -1,9 +1,11 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::same_type_and_consts; + use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; declare_clippy_lint! { /// ### What it does @@ -35,24 +37,40 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { return; } if let ExprKind::Binary(ref cmp, left, right) = e.kind { + let tck = cx.typeck_results(); match cmp.node { BinOpKind::Mul | BinOpKind::BitAnd => { - check(cx, left, e.span); - check(cx, right, e.span); + check(cx, tck, left, right, e); + check(cx, tck, right, left, e); }, - BinOpKind::Div => check(cx, left, e.span), + BinOpKind::Div => check(cx, tck, left, right, e), _ => (), } } } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) { - if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) { +fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { + let input_ty = tck.expr_ty(input).peel_refs(); + let output_ty = tck.expr_ty(output).peel_refs(); + !same_type_and_consts(input_ty, output_ty) +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + tck: &TypeckResults<'tcx>, + op: &Expr<'tcx>, + other: &Expr<'tcx>, + parent: &Expr<'tcx>, +) { + if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { + if different_types(tck, other, parent) { + return; + } span_lint( cx, ERASING_OP, - span, + parent.span, "this operation will always return zero. This is likely not the intended outcome", ); } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 5a4b424710440..b22515a39079a 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; use if_chain::if_chain; @@ -12,6 +13,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -113,6 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { // A type param function ref like `T::f` is not 'static, however // it is if cast like `T::f as fn()`. This seems like a rustc bug. if !substs.types().any(|t| matches!(t.kind(), ty::Param(_))); + let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 6b327b9ce1720..98e5234e0aa94 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::higher::FormatArgsExpn; +use clippy_utils::macros::FormatArgsExpn; use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } else { None }; - if let Some(format_args) = FormatArgsExpn::parse(write_arg); + if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg); then { let calling_macro = // ordering is important here, since `writeln!` uses `write!` internally @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { ) }; let msg = format!("use of `{}.unwrap()`", used); - if let [write_output] = *format_args.format_string_symbols { + if let [write_output] = *format_args.format_string_parts { let mut write_output = write_output.to_string(); if write_output.ends_with('\n') { write_output.pop(); diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index 05d300058cf4f..02f1baf27fae8 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::method_chain_args; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; - use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath}; + use rustc_hir::{Expr, ImplItemKind}; struct FindPanicUnwrap<'a, 'tcx> { lcx: &'a LateContext<'tcx>, @@ -80,14 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.lcx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - then { + if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { + if is_panic(self.lcx, macro_call.def_id) { self.result.push(expr.span); } } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 3f043e5f2f1c5..688d8f8630f3f 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; @@ -43,38 +43,41 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) { - Some(e) if !e.call_site.from_expansion() => e, - _ => return, + let (format_args, call_site) = if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id); + if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn); + then { + (format_args, macro_call.span) + } else { + return + } }; let mut applicability = Applicability::MachineApplicable; if format_args.value_args.is_empty() { - if format_args.format_string_parts.is_empty() { - span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability); - } else { - if_chain! { - if let [e] = &*format_args.format_string_parts; - if let ExprKind::Lit(lit) = &e.kind; - if let Some(s_src) = snippet_opt(cx, lit.span); - then { + match *format_args.format_string_parts { + [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), + [_] => { + if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) { // Simulate macro expansion, converting {{ and }} to { and }. let s_expand = s_src.replace("{{", "{").replace("}}", "}"); let sugg = format!("{}.to_string()", s_expand); span_useless_format(cx, call_site, sugg, applicability); } - } + }, + [..] => {}, } } else if let [value] = *format_args.value_args { if_chain! { - if format_args.format_string_symbols == [kw::Empty]; + if format_args.format_string_parts == [kw::Empty]; if match cx.typeck_results().expr_ty(value).peel_refs().kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did), ty::Str => true, _ => false, }; if let Some(args) = format_args.args(); - if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting()); + if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting()); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index f0e1a67dcddb9..ae423d799d71a 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn}; +use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_diag_trait_item, match_def_path, paths}; @@ -83,7 +83,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_m impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(expr); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr); let expr_expn_data = expr.span.ctxt().outer_expn_data(); let outermost_expn_data = outermost_expn_data(expr_expn_data); if let Some(macro_def_id) = outermost_expn_data.macro_def_id; @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if let Some(args) = format_args.args(); then { for (i, arg) in args.iter().enumerate() { - if !arg.is_display() { + if arg.format_trait != sym::Display { continue; } if arg.has_string_formatting() { @@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if is_aliased(&args, i) { continue; } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg); - check_to_string_in_format_args(cx, name, arg); + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value); + check_to_string_in_format_args(cx, name, arg.value); } } } @@ -122,30 +122,31 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { } } -fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) { - if_chain! { - if FormatExpn::parse(arg.value).is_some(); - if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion(); - then { - span_lint_and_then( - cx, - FORMAT_IN_FORMAT_ARGS, - call_site, - &format!("`format!` in `{}!` args", name), - |diag| { - diag.help(&format!( - "combine the `format!(..)` arguments with the outer `{}!(..)` call", - name - )); - diag.help("or consider changing `format!` to `format_args!`"); - }, - ); - } +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) { + let expn_data = arg.span.ctxt().outer_expn_data(); + if expn_data.call_site.from_expansion() { + return; + } + let Some(mac_id) = expn_data.macro_def_id else { return }; + if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) { + return; } + span_lint_and_then( + cx, + FORMAT_IN_FORMAT_ARGS, + call_site, + &format!("`format!` in `{}!` args", name), + |diag| { + diag.help(&format!( + "combine the `format!(..)` arguments with the outer `{}!(..)` call", + name + )); + diag.help("or consider changing `format!` to `format_args!`"); + }, + ); } -fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) { - let value = arg.value; +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { if_chain! { if !value.span.from_expansion(); if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind; diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 866ff216f84a2..5ece2cc5ac4f7 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -53,7 +53,7 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); -impl LateLintPass<'_> for FromOverInto { +impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { return; diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 73e800073b03f..57b0751320521 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); -impl LateLintPass<'tcx> for FromStrRadix10 { +impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if_chain! { if let ExprKind::Call(maybe_path, arguments) = &exp.kind; diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 77d08081c07f1..f2b4aefaead52 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -18,7 +18,7 @@ use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { @@ -40,7 +40,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -62,7 +62,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index f83789bb2199e..6d829a18b2e09 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -9,7 +9,7 @@ use clippy_utils::{iter_input_pats, path_to_local}; use super::NOT_UNSAFE_PTR_ARG_DEREF; -pub(super) fn check_fn( +pub(super) fn check_fn<'tcx>( cx: &LateContext<'tcx>, kind: intravisit::FnKind<'tcx>, decl: &'tcx hir::FnDecl<'tcx>, @@ -25,14 +25,14 @@ pub(super) fn check_fn( check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id)); } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id); } } -fn check_raw_ptr( +fn check_raw_ptr<'tcx>( cx: &LateContext<'tcx>, unsafety: hir::Unsafety, decl: &'tcx hir::FnDecl<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs index 71f6f87ae602b..73f08a0498973 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs @@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use super::RESULT_UNIT_ERR; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -23,7 +23,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -33,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs index 63a14d8d4cde8..3af960491ed01 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs @@ -9,9 +9,9 @@ use clippy_utils::is_trait_impl_item; use super::TOO_MANY_ARGUMENTS; pub(super) fn check_fn( - cx: &LateContext<'tcx>, - kind: intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, + cx: &LateContext<'_>, + kind: intravisit::FnKind<'_>, + decl: &hir::FnDecl<'_>, span: Span, hir_id: hir::HirId, too_many_arguments_threshold: u64, @@ -39,11 +39,7 @@ pub(super) fn check_fn( } } -pub(super) fn check_trait_item( - cx: &LateContext<'tcx>, - item: &'tcx hir::TraitItem<'_>, - too_many_arguments_threshold: u64, -) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { // don't lint extern functions decls, it's not their fault if sig.header.abi == Abi::Rust { diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs index 65efbbab41a46..54bdea7ea25d6 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs @@ -11,9 +11,9 @@ use super::TOO_MANY_LINES; pub(super) fn check_fn( cx: &LateContext<'_>, - kind: FnKind<'tcx>, + kind: FnKind<'_>, span: Span, - body: &'tcx hir::Body<'_>, + body: &hir::Body<'_>, too_many_lines_threshold: u64, ) { // Closures must be contained in a parent body, which will be checked for `too_many_lines`. diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index 16e5c5ca603db..9525c163ece17 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -55,7 +55,7 @@ impl IfThenSomeElseNone { impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); -impl LateLintPass<'_> for IfThenSomeElseNone { +impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { return; diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index 07caeada80d00..d650d6e9a8587 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -94,8 +94,8 @@ fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> { } fn lint_implicit_returns( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, + cx: &LateContext<'_>, + expr: &Expr<'_>, // The context of the function body. ctxt: SyntaxContext, // Whether the expression is from a macro expansion. diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 388bb3727f96c..3d44a669d8f05 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); -impl LateLintPass<'_> for InconsistentStructConstructor { +impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion(); diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 69f1c90beec5d..073313e2bad4e 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -69,7 +69,7 @@ impl IndexRefutableSlice { impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); -impl LateLintPass<'_> for IndexRefutableSlice { +impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs index 5fe6725b581dc..9284e00240992 100644 --- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs +++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -46,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Struct(path, fields, None) = e.kind { if !fields.is_empty() - && !in_macro(e.span) + && !e.span.from_expansion() && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index 0af6b3b7d464e..d3bdc819a9f2b 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -1,8 +1,7 @@ -use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait}; -use rustc_hir::{ImplItem, ImplItemKind}; +use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait}; +use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::kw; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -40,26 +39,52 @@ declare_clippy_lint! { declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); -impl LateLintPass<'_> for IterNotReturningIterator { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { - let name = impl_item.ident.name.as_str(); - if_chain! { - if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind; - let ret_ty = return_ty(cx, impl_item.hir_id()); - if matches!(name, "iter" | "iter_mut"); - if let [param] = cx.tcx.fn_arg_names(impl_item.def_id); - if param.name == kw::SelfLower; - if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if !implements_trait(cx, ret_ty, iter_trait_id, &[]); +impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") { + if let TraitItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); + } + } + } - then { - span_lint( - cx, - ITER_NOT_RETURNING_ITERATOR, - fn_sig.span, - &format!("this method is named `{}` but its return type does not implement `Iterator`", name), - ); + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") + && !matches!( + get_parent_node(cx.tcx, item.hir_id()), + Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some() + ) + { + if let ImplItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); } } } } + +fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { + if sig.decl.implicit_self.has_implicit_self() { + let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output(); + let ret_ty = cx + .tcx + .try_normalize_erasing_regions(cx.param_env, ret_ty) + .unwrap_or(ret_ty); + if cx + .tcx + .get_diagnostic_item(sym::Iterator) + .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[])) + { + span_lint( + cx, + ITER_NOT_RETURNING_ITERATOR, + sig.span, + &format!( + "this method is named `{}` but its return type does not implement `Iterator`", + name + ), + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 64f6d62fbdcd8..e1168c3f6022e 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -245,7 +245,7 @@ enum LenOutput<'tcx> { Option(DefId), Result(DefId, Ty<'tcx>), } -fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> { +fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> { match *sig.output().kind() { ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => { diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 944411087e951..26fb4259952b6 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(double_comparison::DOUBLE_COMPARISONS), @@ -113,6 +115,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_strip::MANUAL_STRIP), @@ -204,7 +207,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index 002122793f3b6..746bdb19c3d92 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -3,33 +3,33 @@ // Manual edits will be overwritten. store.register_lints(&[ - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::IF_CHAIN_STYLE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::PRODUCE_ICE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, approx_const::APPROX_CONSTANT, @@ -59,6 +59,7 @@ store.register_lints(&[ bool_assert_comparison::BOOL_ASSERT_COMPARISON, booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, + borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, cargo_common_metadata::CARGO_COMMON_METADATA, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, @@ -225,6 +226,7 @@ store.register_lints(&[ main_recursion::MAIN_RECURSION, manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, + manual_bits::MANUAL_BITS, manual_map::MANUAL_MAP, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, @@ -435,6 +437,7 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, + single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs index e3cf067001834..a735379010026 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs @@ -6,8 +6,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), @@ -17,6 +15,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(let_if_seq::USELESS_LET_IF_SEQ), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), + LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs index 70a4a62437890..1744b7c825078 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::VERBOSE_BIT_MASK), + LintId::of(borrow_as_ptr::BORROW_AS_PTR), LintId::of(bytecount::NAIVE_BYTECOUNT), LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(casts::CAST_LOSSLESS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs index 2ea0b696f1feb..f9ffd4cf829fa 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs @@ -19,7 +19,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::SINGLE_CHAR_PATTERN), LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(misc::CMP_OWNED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs index eab389a9bd892..e7e2798da7da9 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs @@ -54,6 +54,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(shadow::SHADOW_REUSE), LintId::of(shadow::SHADOW_SAME), LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES), LintId::of(strings::STRING_ADD), LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs index 1a0b869d40adb..05211476ff230 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs @@ -16,6 +16,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(enum_variants::ENUM_VARIANT_NAMES), @@ -41,6 +43,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d4687a1e28797..79e9882fef4c4 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -4,7 +4,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(drain_filter)] -#![feature(in_band_lifetimes)] #![feature(iter_intersperse)] #![feature(let_else)] #![feature(once_cell)] @@ -153,12 +152,9 @@ macro_rules! declare_clippy_lint { }; } -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] mod deprecated_lints; -#[cfg_attr( - any(feature = "internal-lints", feature = "metadata-collector-lint"), - allow(clippy::missing_clippy_version_attribute) -)] +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` @@ -177,6 +173,7 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; +mod borrow_as_ptr; mod bytecount; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; @@ -264,6 +261,7 @@ mod macro_use; mod main_recursion; mod manual_assert; mod manual_async_fn; +mod manual_bits; mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; @@ -351,6 +349,7 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; mod slow_vector_initialization; @@ -471,7 +470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_restriction.rs"); include!("lib.register_pedantic.rs"); - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] include!("lib.register_internal.rs"); include!("lib.register_all.rs"); @@ -483,7 +482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_cargo.rs"); include!("lib.register_nursery.rs"); - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); @@ -492,7 +491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } // all the internal lints - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); @@ -858,6 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); + store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); + store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); + store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs index dda09fecdf90f..823cf0f43221c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate}; use rustc_hir::{Block, Expr}; use rustc_lint::LateContext; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) { diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs index 1bab0d99b695c..17246cc5426ae 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -8,7 +8,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::symbol::sym; -pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { let self_ty = cx.typeck_results().expr_ty(self_arg); let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) { diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index c62fa5e998bd4..48c4015e07b6d 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -2,7 +2,7 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_copy; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use if_chain::if_chain; use rustc_ast::ast; @@ -62,15 +62,15 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Index(base_left, idx_left) = lhs.kind; if let ExprKind::Index(base_right, idx_right) = rhs.kind; - if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); - if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); + if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); + if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts); // Source and destination must be different if path_to_local(base_left) != path_to_local(base_right); then { - Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, + Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) } else { None @@ -78,7 +78,7 @@ pub(super) fn check<'tcx>( } }) }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) + .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) .collect::<Option<Vec<_>>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); @@ -105,6 +105,7 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, + elem_ty: Ty<'tcx>, dst: &IndexExpr<'_>, src: &IndexExpr<'_>, ) -> String { @@ -187,9 +188,16 @@ fn build_manual_memcpy_suggestion<'tcx>( .into() }; + let method_str = if is_copy(cx, elem_ty) { + "copy_from_slice" + } else { + "clone_from_slice" + }; + format!( - "{}.clone_from_slice(&{}[{}..{}]);", + "{}.{}(&{}[{}..{}]);", dst, + method_str, src_base_str, src_offset.maybe_par(), src_limit.maybe_par() @@ -203,7 +211,7 @@ fn build_manual_memcpy_suggestion<'tcx>( #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); -impl Display for MinifyingSugg<'a> { +impl<'a> Display for MinifyingSugg<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } @@ -324,14 +332,13 @@ struct Start<'hir> { kind: StartKind<'hir>, } -fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind() { - ty::Ref(_, subty, _) => is_slice_like(cx, subty), - ty::Slice(..) | ty::Array(..) => true, - _ => false, - }; - - is_slice || is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::VecDeque) +fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { + match ty.kind() { + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)), + ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty), + ty::Slice(ty) | ty::Array(ty, _) => Some(ty), + _ => None, + } } fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index aedf0844937d1..37a57d8feb1d3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -147,7 +147,7 @@ impl BreakAfterExprVisitor { } } -impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor { +impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs index ba895f35faa26..6248680aa6216 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -339,8 +339,8 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( } } -fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet { - fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { +fn get_captured_ids(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet { + fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { match ty.kind() { ty::Adt(_, generics) => { for generic in *generics { diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index a3aa6be6afd64..bb1b3e2a1ec6a 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -10,8 +10,8 @@ use rustc_span::Span; use std::iter::{once, Iterator}; pub(super) fn check( - cx: &LateContext<'tcx>, - block: &'tcx Block<'_>, + cx: &LateContext<'_>, + block: &Block<'_>, loop_id: HirId, span: Span, for_loop: Option<&ForLoop<'_>>, diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 4dcd5c87722ee..8f57df0be6bd4 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { // extract the expression from the first statement (if any) in a block let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); // or extract the first expression (if any) from the block diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index b390476a664d9..750328d1d01a5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -8,12 +8,13 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_span::{symbol::sym, Span, Symbol}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_span::{symbol::sym, Symbol}; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; @@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // get the loop containing the match expression if !uses_iter(cx, &iter_expr_struct, if_then); then { - (let_expr, iter_expr_struct, some_pat, expr) + (let_expr, iter_expr_struct, iter_expr, some_pat, expr) } else { return; } @@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used // afterwards a mutable borrow of a field isn't necessary. - let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) + || !iter_expr_struct.can_move + || !iter_expr_struct.fields.is_empty() + || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr) + { ".by_ref()" } else { "" @@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { #[derive(Debug)] struct IterExpr { - /// The span of the whole expression, not just the path and fields stored here. - span: Span, /// The fields used, in order of child to parent. fields: Vec<Symbol>, /// The path being used. path: Res, + /// Whether or not the iterator can be moved. + can_move: bool, } /// Parses any expression to find out which field of which variable is used. Will return `None` if /// the expression might have side effects. fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> { - let span = e.span; let mut fields = Vec::new(); + let mut can_move = true; loop { + if cx + .typeck_results() + .expr_adjustments(e) + .iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + // Custom deref impls need to borrow the whole value as it's captured by reference + can_move = false; + fields.clear(); + } match e.kind { ExprKind::Path(ref path) => { break Some(IterExpr { - span, fields, path: cx.qpath_res(path, e.hir_id), + can_move, }); }, ExprKind::Field(base, name) => { @@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have // already been seen. ExprKind::Index(base, idx) if !idx.can_have_side_effects() => { + can_move = false; fields.clear(); e = base; }, ExprKind::Unary(UnOp::Deref, base) => { + can_move = false; fields.clear(); e = base; }, @@ -174,7 +191,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie /// Strips off all field and path expressions. This will return true if a field or path has been /// skipped. Used to skip them after failing to check for equality. -fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { +fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { let mut e = expr; let e = loop { match e.kind { @@ -187,13 +204,13 @@ fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool } /// Checks if the given expression uses the iterator. -fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { +fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { struct V<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, uses_iter: bool, } - impl Visitor<'tcx> for V<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -228,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr } #[allow(clippy::too_many_lines)] -fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool { +fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool { struct AfterLoopVisitor<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, @@ -236,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: after_loop: bool, used_iter: bool, } - impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -275,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: found_local: bool, used_after: bool, } - impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 41f5a913b316e..ca1ccb93bf3fb 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; @@ -104,34 +103,34 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { } } } else { - if in_macro(item.span) { + if item.span.from_expansion() { self.push_unique_macro_pat_ty(cx, item.span); } } } } fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { - if in_macro(attr.span) { + if attr.span.from_expansion() { self.push_unique_macro(cx, attr.span); } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - if in_macro(expr.span) { + if expr.span.from_expansion() { self.push_unique_macro(cx, expr.span); } } fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { - if in_macro(stmt.span) { + if stmt.span.from_expansion() { self.push_unique_macro(cx, stmt.span); } } fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { - if in_macro(pat.span) { + if pat.span.from_expansion() { self.push_unique_macro_pat_ty(cx, pat.span); } } fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) { - if in_macro(ty.span) { + if ty.span.from_expansion() { self.push_unique_macro_pat_ty(cx, ty.span); } } diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 5a2a965716cc6..26b53ab5d6837 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::PanicExpn; +use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, sugg}; +use clippy_utils::{peel_blocks_with_stmt, sugg}; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp}; +use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -34,65 +35,34 @@ declare_clippy_lint! { declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); -impl LateLintPass<'_> for ManualAssert { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for ManualAssert { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if_chain! { - if let Expr { - kind: ExprKind:: If(cond, Expr { - kind: ExprKind::Block( - Block { - stmts: [stmt], - .. - }, - _), - .. - }, None), - .. - } = &expr; - if is_expn_of(stmt.span, "panic").is_some(); + if let ExprKind::If(cond, then, None) = expr.kind; if !matches!(cond.kind, ExprKind::Let(_)); - if let StmtKind::Semi(semi) = stmt.kind; + if !expr.span.from_expansion(); + let then = peel_blocks_with_stmt(then); + if let Some(macro_call) = root_macro_call(then.span); + if cx.tcx.item_name(macro_call.def_id) == sym::panic; if !cx.tcx.sess.source_map().is_multiline(cond.span); - + if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); then { - let call = if_chain! { - if let ExprKind::Block(block, _) = semi.kind; - if let Some(init) = block.expr; - then { - init - } else { - semi - } - }; - let span = if let Some(panic_expn) = PanicExpn::parse(call) { - match *panic_expn.format_args.value_args { - [] => panic_expn.format_args.format_string_span, - [.., last] => panic_expn.format_args.format_string_span.to(last.span), - } - } else if let ExprKind::Call(_, [format_args]) = call.kind { - format_args.span - } else { - return - }; let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); - let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind { - if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { - sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par()) - } - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par()) + let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); + let cond = cond.peel_drop_temps(); + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), }; - + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); span_lint_and_sugg( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", "try", - format!("assert!({}, {});", cond_sugg, sugg), + sugg, Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs new file mode 100644 index 0000000000000..50bf2527e39a8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -0,0 +1,107 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for uses of `std::mem::size_of::<T>() * 8` when + /// `T::BITS` is available. + /// + /// ### Why is this bad? + /// Can be written as the shorter `T::BITS`. + /// + /// ### Example + /// ```rust + /// std::mem::size_of::<usize>() * 8; + /// ``` + /// Use instead: + /// ```rust + /// usize::BITS; + /// ``` + #[clippy::version = "1.60.0"] + pub MANUAL_BITS, + style, + "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`" +} + +#[derive(Clone)] +pub struct ManualBits { + msrv: Option<RustcVersion>, +} + +impl ManualBits { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualBits => [MANUAL_BITS]); + +impl<'tcx> LateLintPass<'tcx> for ManualBits { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) { + return; + } + + if_chain! { + if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; + if let BinOpKind::Mul = &bin_op.node; + if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); + if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); + if let ExprKind::Lit(lit) = &other_expr.kind; + if let LitKind::Int(8, _) = lit.node; + + then { + span_lint_and_sugg( + cx, + MANUAL_BITS, + expr.span, + "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits", + "consider using", + format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn get_one_size_of_ty<'tcx>( + cx: &LateContext<'tcx>, + expr1: &'tcx Expr<'_>, + expr2: &'tcx Expr<'_>, +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> { + match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) { + (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)), + (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)), + _ => None, + } +} + +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { + if_chain! { + if let ExprKind::Call(count_func, _func_args) = expr.kind; + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + + if let QPath::Resolved(_, count_func_path) = count_func_qpath; + if let Some(segment_zero) = count_func_path.segments.get(0); + if let Some(args) = segment_zero.args; + if let Some(GenericArg::Type(real_ty)) = args.args.get(0); + + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty)) + } else { + None + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs index 34a70ca76c6a2..8475e367b09fe 100644 --- a/src/tools/clippy/clippy_lints/src/manual_map.rs +++ b/src/tools/clippy/clippy_lints/src/manual_map.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); -impl LateLintPass<'_> for ManualMap { +impl<'tcx> LateLintPass<'tcx> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { @@ -219,7 +219,7 @@ impl LateLintPass<'_> for ManualMap { // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. -fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) @@ -251,8 +251,13 @@ struct SomeExpr<'tcx> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { - fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { +fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option<OptionPat<'tcx>> { match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), @@ -269,7 +274,7 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr( +fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, needs_unsafe_block: bool, @@ -306,6 +311,6 @@ fn get_some_expr( } // Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) } diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs index b60e2dc366b41..bd083e3e9e201 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); -impl LateLintPass<'_> for ManualOkOr { +impl<'tcx> LateLintPass<'tcx> for ManualOkOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), scrutinee.span) { return; diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs index aac3c6e0de2bb..b3a91d9f18f5d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for ManualUnwrapOr { +impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { return; diff --git a/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs index 2c0fc218ca07c..1fc7eb7214284 100644 --- a/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs @@ -55,7 +55,7 @@ enum CaseMethod { AsciiUppercase, } -impl LateLintPass<'_> for MatchStrCaseMismatch { +impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, expr.span); diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index 5fa8f249e701e..60dd957db01f8 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; -use clippy_utils::higher; +use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, + get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, strip_pat_refs, }; +use clippy_utils::{higher, peel_blocks_with_stmt}; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use core::iter::{once, ExactSizeIterator}; use if_chain::if_chain; @@ -974,7 +975,8 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } if_chain! { if matching_wild; - if is_panic_call(arm.body); + if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span); + if is_panic(cx, macro_call.def_id); then { // `Err(_)` or `Err(_e)` arm with `panic!` found span_lint_and_note(cx, @@ -997,7 +999,7 @@ enum CommonPrefixSearcher<'a> { Path(&'a [PathSegment<'a>]), Mixed, } -impl CommonPrefixSearcher<'a> { +impl<'a> CommonPrefixSearcher<'a> { fn with_path(&mut self, path: &'a [PathSegment<'a>]) { match path { [path @ .., _] => self.with_prefix(path), @@ -1179,22 +1181,6 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) }; } -// If the block contains only a `panic!` macro (as expression or statement) -fn is_panic_call(expr: &Expr<'_>) -> bool { - // Unwrap any wrapping blocks - let span = if let ExprKind::Block(block, _) = expr.kind { - match (&block.expr, block.stmts.len(), block.stmts.first()) { - (&Some(exp), 0, _) => exp.span, - (&None, 1, Some(stmt)) => stmt.span, - _ => return false, - } - } else { - expr.span - }; - - is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none() -} - fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>) where 'b: 'a, @@ -1818,11 +1804,15 @@ mod redundant_pattern_match { /// Checks if the drop order for a type matters. Some std types implement drop solely to /// deallocate memory. For these types, and composites containing them, changing the drop order /// won't result in any observable side effects. - fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } - fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool { + fn type_needs_ordered_drop_inner<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + seen: &mut FxHashSet<Ty<'tcx>>, + ) -> bool { if !seen.insert(ty) { return false; } @@ -1884,7 +1874,7 @@ mod redundant_pattern_match { // Checks if there are any temporaries created in the given expression for which drop order // matters. - fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, res: bool, diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 0ec9387f9c460..0f39470f34262 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; @@ -14,7 +14,13 @@ use super::EXPECT_FUN_CALL; /// Checks for the `EXPECT_FUN_CALL` lint. #[allow(clippy::too_many_lines)] -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &'tcx [hir::Expr<'tcx>], +) { // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or // `&str` fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { @@ -128,11 +134,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa let mut applicability = Applicability::MachineApplicable; //Special handling for `format!` as arg_root - if let Some(format_expn) = FormatExpn::parse(arg_root) { - let span = match *format_expn.format_args.value_args { - [] => format_expn.format_args.format_string_span, - [.., last] => format_expn.format_args.format_string_span.to(last.span), - }; + if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { + if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { + return; + } + let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return }; + let span = format_args.inputs_span(); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 8ea9312c0f707..6436e28a63c52 100644 --- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp } } -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { +fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String { fn strip_angle_brackets(s: &str) -> Option<&str> { s.strip_prefix('<')?.strip_suffix('>') } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 4e33b2ff14cde..1041f644e32eb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -80,7 +80,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; @@ -1895,6 +1894,11 @@ declare_clippy_lint! { /// ### Why is this bad? /// The unnecessary calls result in useless allocations. /// + /// ### Known problems + /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an + /// owned copy of a resource and the resource is later used mutably. See + /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148). + /// /// ### Example /// ```rust /// let path = std::path::Path::new("x"); @@ -1997,24 +2001,16 @@ impl_lint_pass!(Methods => [ ]); /// Extracts a method call name, args, and `Span` of the method name. -fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> { +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> { if let ExprKind::MethodCall(path, span, args, _) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) { - return Some((path.ident.name, args, span)); + let name = path.ident.name.as_str(); + return Some((name, args, span)); } } None } -/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str` -macro_rules! method_call { - ($expr:expr) => { - method_call($expr) - .as_ref() - .map(|&(ref name, args, span)| (name.as_str(), args, span)) - }; -} - impl<'tcx> LateLintPass<'tcx> for Methods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if expr.span.from_expansion() { @@ -2217,7 +2213,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { - if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { + if let Some((name, [recv, args @ ..], span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -2233,7 +2229,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), - ("collect", []) => match method_call!(recv) { + ("collect", []) => match method_call(recv) { Some((name @ ("cloned" | "copied"), [recv2], _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, @@ -2247,14 +2243,14 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("count", []) => match method_call!(recv) { + ("count", []) => match method_call(recv) { Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { iter_count::check(cx, expr, recv2, name); }, Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), _ => {}, }, - ("expect", [_]) => match method_call!(recv) { + ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), _ => expect_used::check(cx, expr, recv), }, @@ -2271,13 +2267,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => { - if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { + if let Some(("map", [recv, map_arg], _)) = method_call(recv) { map_flatten::check(cx, expr, recv, map_arg); } }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", [_, _], span2)) = method_call!(recv) { + if let Some(("inspect", [_, _], span2)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -2286,7 +2282,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("map", [m_arg]) => { - if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) { + if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), @@ -2301,7 +2297,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), ("next", []) => { - if let Some((name, [recv, args @ ..], _)) = method_call!(recv) { + if let Some((name, [recv, args @ ..], _)) = method_call(recv) { match (name, args) { ("filter", [arg]) => filter_next::check(cx, expr, recv, arg), ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), @@ -2312,7 +2308,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, - ("nth", [n_arg]) => match method_call!(recv) { + ("nth", [n_arg]) => match method_call(recv) { Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), @@ -2344,12 +2340,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv, span); }, - ("unwrap", []) => match method_call!(recv) { + ("unwrap", []) => match method_call(recv) { Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), _ => unwrap_used::check(cx, expr, recv), }, - ("unwrap_or", [u_arg]) => match method_call!(recv) { + ("unwrap_or", [u_arg]) => match method_call(recv) { Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, @@ -2358,7 +2354,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("unwrap_or_else", [u_arg]) => match method_call!(recv) { + ("unwrap_or_else", [u_arg]) => match method_call(recv) { Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -2371,7 +2367,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) { + if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } @@ -2535,11 +2531,17 @@ impl SelfKind { implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) + } + match self { Self::Value => matches_value(cx, parent_ty, ty), Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => ty != parent_ty, + Self::No => matches_none(cx, parent_ty, ty), } } diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 70f20da1d6db7..514bdadc442e3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -124,7 +124,7 @@ struct IterUsage { } #[allow(clippy::too_many_lines)] -fn parse_iter_usage( +fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, @@ -281,7 +281,7 @@ pub(super) fn check_needless_splitn( } } -fn check_iter( +fn check_iter<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 8300df03e9935..5999245ea7d00 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -12,13 +12,13 @@ use rustc_span::{sym, Symbol}; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); if is_into_iter(cx, callee_def_id); then { - check_for_loop_iter(cx, parent, method_name, receiver) + check_for_loop_iter(cx, parent, method_name, receiver, false) } else { false } @@ -30,10 +30,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// include this code directly is so that it can be called from /// `unnecessary_into_owned::check_into_iter_call_arg`. pub fn check_for_loop_iter( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, + cloned_before_iter: bool, ) -> bool { if_chain! { if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); @@ -70,12 +71,22 @@ pub fn check_for_loop_iter( expr.span, &format!("unnecessary use of `{}`", method_name), |diag| { - diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable); + // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to + // a `to_owned`-like function was removed, then the next suggestion may be + // incorrect. This is because the iterator that results from the call's removal + // could hold a reference to a resource that is used mutably. See + // https://github.com/rust-lang/rust-clippy/issues/8148. + let applicability = if cloned_before_iter { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + diag.span_suggestion(expr.span, "use", snippet, applicability); for addr_of_expr in addr_of_exprs { match addr_of_expr.kind { ExprKind::AddrOf(_, _, referent) => { let span = addr_of_expr.span.with_hi(referent.span.lo()); - diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable); + diag.span_suggestion(span, "remove this `&`", String::new(), applicability); } _ => unreachable!(), } @@ -90,7 +101,7 @@ pub fn check_for_loop_iter( /// The core logic of `check_for_loop_iter` above, this function wraps a use of /// `CloneOrCopyVisitor`. -fn clone_or_copy_needed( +fn clone_or_copy_needed<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, body: &'tcx Expr<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index c48bacfce0d37..e5b6d296b2d25 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -16,7 +16,7 @@ use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -44,11 +44,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// call of a `to_owned`-like function is unnecessary. #[allow(clippy::too_many_lines)] fn check_addr_of_expr( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, method_def_id: DefId, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -171,12 +171,7 @@ fn check_addr_of_expr( /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -fn check_into_iter_call_arg( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - method_name: Symbol, - receiver: &'tcx Expr<'tcx>, -) -> bool { +fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -187,7 +182,13 @@ fn check_into_iter_call_arg( if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) { + if unnecessary_iter_cloned::check_for_loop_iter( + cx, + parent, + method_name, + receiver, + true, + ) { return true; } let cloned_or_copied = if is_copy(cx, item_ty) { @@ -195,6 +196,9 @@ fn check_into_iter_call_arg( } else { "cloned" }; + // The next suggestion may be incorrect because the removal of the `to_owned`-like + // function could cause the iterator to hold a reference to a resource that is used + // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. span_lint_and_sugg( cx, UNNECESSARY_TO_OWNED, @@ -202,7 +206,7 @@ fn check_into_iter_call_arg( &format!("unnecessary use of `{}`", method_name), "use", format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); return true; } @@ -212,7 +216,7 @@ fn check_into_iter_call_arg( /// Checks whether `expr` is an argument in a function call and, if so, determines whether its call /// of a `to_owned`-like function is unnecessary. -fn check_other_call_arg( +fn check_other_call_arg<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, @@ -278,7 +282,7 @@ fn check_other_call_arg( /// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such /// expression found (if any) along with the immediately prior expression. -fn skip_addr_of_ancestors( +fn skip_addr_of_ancestors<'tcx>( cx: &LateContext<'tcx>, mut expr: &'tcx Expr<'tcx>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { @@ -294,7 +298,7 @@ fn skip_addr_of_ancestors( /// Checks whether an expression is a function or method call and, if so, returns its `DefId`, /// `Substs`, and arguments. -fn get_callee_substs_and_args( +fn get_callee_substs_and_args<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> { @@ -319,7 +323,7 @@ fn get_callee_substs_and_args( } /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type. -fn get_input_traits_and_projections( +fn get_input_traits_and_projections<'tcx>( cx: &LateContext<'tcx>, callee_def_id: DefId, input: Ty<'tcx>, @@ -359,7 +363,11 @@ fn get_input_traits_and_projections( } /// Composes two substitutions by applying the latter to the types of the former. -fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> { +fn compose_substs<'tcx>( + cx: &LateContext<'tcx>, + left: &[GenericArg<'tcx>], + right: SubstsRef<'tcx>, +) -> Vec<GenericArg<'tcx>> { left.iter() .map(|arg| { if let GenericArgKind::Type(arg_ty) = arg.unpack() { diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 401dc27811dc3..21b3f81d5d981 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -717,7 +717,7 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) } } -fn check_binary( +fn check_binary<'a>( cx: &LateContext<'a>, expr: &Expr<'_>, cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>, diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs index 12e219cd5c871..842959ce36b07 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{higher, is_direct_expn_of}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -34,26 +34,30 @@ declare_clippy_lint! { declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]); -const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - for dmn in &DEBUG_MACRO_NAMES { - if is_direct_expn_of(e.span, dmn).is_some() { - if let Some(macro_args) = higher::extract_assert_macro_args(e) { - for arg in macro_args { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(arg); - if let Some(span) = visitor.expr_span() { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - &format!("do not call a function with mutable arguments inside of `{}!`", dmn), - ); - } - } - } + let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let macro_name = cx.tcx.item_name(macro_call.def_id); + if !matches!( + macro_name.as_str(), + "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + ) { + return; + } + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return }; + for arg in [lhs, rhs] { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(arg); + if let Some(span) = visitor.expr_span() { + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &format!( + "do not call a function with mutable arguments inside of `{}!`", + macro_name + ), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 816377fe65e9f..73823779e493d 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, - perf, + nursery, "using a mutex where an atomic value could be used instead" } diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 0c1da0351739a..19d58f7474b0a 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); -impl LateLintPass<'_> for NeedlessForEach { +impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { let expr = match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 094a3f111ba5a..9957afcbf04aa 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -330,7 +330,7 @@ fn check<'tcx>( Some(()) } -impl LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 9d5babc5de840..5bf8a1ba1ca30 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } } -fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool { +fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); @@ -155,7 +155,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { +fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; if let Some(reduced) = reduce_expression(cx, expr); diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 4b57dbc4c412a..e46fee4cac5ee 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); -impl LateLintPass<'_> for NonOctalUnixPermissions { +impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, _, [func, param], _) => { diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index 9c97143764545..e0da12f77fcc7 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]); impl EarlyLintPass for OctalEscapes { - fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if in_external_macro(cx.sess, expr.span) { return; } @@ -65,7 +65,7 @@ impl EarlyLintPass for OctalEscapes { } } -fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) { +fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { let contents = lit.symbol.as_str(); let mut iter = contents.char_indices().peekable(); let mut found = vec![]; diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index 8769c04521464..b7a56970b3355 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{find_macro_calls, is_expn_of, return_ty}; +use clippy_utils::visitors::expr_visitor_no_bodies; use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; +use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -55,19 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = find_macro_calls( - &[ - "unimplemented", - "unreachable", - "panic", - "todo", - "assert", - "assert_eq", - "assert_ne", - ], - body, - ); - panics.retain(|span| is_expn_of(*span, "debug_assert").is_none()); + let mut panics = Vec::new(); + expr_visitor_no_bodies(|expr| { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; + if matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" + ) { + panics.push(macro_call.span); + return false; + } + true + }) + .visit_expr(&body.value); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index edfac824ded98..6ef6b9a20aa4b 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,10 +1,8 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expn_of, match_panic_call}; -use if_chain::if_chain; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -78,37 +76,37 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if match_panic_call(cx, expr).is_some() - && (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none()) - { - let span = get_outer_span(expr); - if is_expn_of(expr.span, "unimplemented").is_some() { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + if is_panic(cx, macro_call.def_id) { + span_lint( + cx, + PANIC, + macro_call.span, + "`panic` should not be present in production code", + ); + return; + } + match cx.tcx.item_name(macro_call.def_id).as_str() { + "todo" => { + span_lint( + cx, + TODO, + macro_call.span, + "`todo` should not be present in production code", + ); + }, + "unimplemented" => { span_lint( cx, UNIMPLEMENTED, - span, + macro_call.span, "`unimplemented` should not be present in production code", ); - } else if is_expn_of(expr.span, "todo").is_some() { - span_lint(cx, TODO, span, "`todo` should not be present in production code"); - } else if is_expn_of(expr.span, "unreachable").is_some() { - span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro"); - } else if is_expn_of(expr.span, "panic").is_some() { - span_lint(cx, PANIC, span, "`panic` should not be present in production code"); - } - } - } -} - -fn get_outer_span(expr: &Expr<'_>) -> Span { - if_chain! { - if expr.span.from_expansion(); - let first = expr.span.ctxt().outer_expn_data().call_site; - if first.from_expansion(); - then { - first.ctxt().outer_expn_data().call_site - } else { - expr.span + }, + "unreachable" => { + span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); + }, + _ => {}, } } } diff --git a/src/tools/clippy/clippy_lints/src/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/ptr_eq.rs index 3c126fc1ca69a..2bec93ac60605 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_eq.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_eq.rs @@ -39,7 +39,7 @@ declare_lint_pass!(PtrEq => [PTR_EQ]); static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; -impl LateLintPass<'_> for PtrEq { +impl<'tcx> LateLintPass<'tcx> for PtrEq { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index 1cf349f8aa7c7..1991a01fb60be 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { // cloned value is mutated, and the clone is alive. - if possible_borrower.is_alive_at(ret_local, loc) { + if possible_borrower.local_is_alive_at(ret_local, loc) { continue; } } @@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> { self.bitset.0 == self.bitset.1 } - fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { self.maybe_live.seek_after_primary_effect(at); self.maybe_live.contains(local) } diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index b2bd0103d1114..7c88b42ea3199 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); -impl LateLintPass<'_> for RedundantSlicing { +impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index 22ae7a291d00e..b24483723700c 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; + if deref_target.span.ctxt() == e.span.ctxt(); if !addrof_target.span.from_expansion(); then { let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index b57ec96bc7e6d..5dafd08cf3be0 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -1,5 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty}; +use clippy_utils::{nth_arg, return_ty}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind}; @@ -13,25 +14,46 @@ declare_clippy_lint! { /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute. /// /// ### Why is this bad? - /// It prevents to "forget" to use the newly created value. + /// Methods returning `Self` often create new values, having the `#[must_use]` attribute + /// prevents users from "forgetting" to use the newly created value. + /// + /// The `#[must_use]` attribute can be added to the type itself to ensure that instances + /// are never forgotten. Functions returning a type marked with `#[must_use]` will not be + /// linted, as the usage is already enforced by the type attribute. /// /// ### Limitations /// This lint is only applied on methods taking a `self` argument. It would be mostly noise /// if it was added on constructors for example. /// /// ### Example + /// Missing attribute /// ```rust /// pub struct Bar; - /// /// impl Bar { /// // Bad /// pub fn bar(&self) -> Self { /// Self /// } + /// } + /// ``` /// - /// // Good + /// It's better to have the `#[must_use]` attribute on the method like this: + /// ```rust + /// pub struct Bar; + /// impl Bar { /// #[must_use] - /// pub fn foo(&self) -> Self { + /// pub fn bar(&self) -> Self { + /// Self + /// } + /// } + /// ``` + /// + /// Or on the type definition like this: + /// ```rust + /// #[must_use] + /// pub struct Bar; + /// impl Bar { + /// pub fn bar(&self) -> Self { /// Self /// } /// } @@ -44,7 +66,7 @@ declare_clippy_lint! { declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]); -fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) { +fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) { if_chain! { // If it comes from an external macro, better ignore it. if !in_external_macro(cx.sess(), span); @@ -65,11 +87,13 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD if !is_must_use_ty(cx, ret_ty); then { - span_lint( + span_lint_and_help( cx, RETURN_SELF_NOT_MUST_USE, span, "missing `#[must_use]` attribute on a method returning `Self`", + None, + "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type" ); } } diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs index 0b3bbbc815580..729694da46d5c 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]); -impl LateLintPass<'_> for SemicolonIfNothingReturned { +impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if_chain! { if !block.span.from_expansion(); diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs new file mode 100644 index 0000000000000..ee82666b5affe --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{GenericParam, GenericParamKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for lifetimes with names which are one character + /// long. + /// + /// ### Why is this bad? + /// A single character is likely not enough to express the + /// purpose of a lifetime. Using a longer name can make code + /// easier to understand, especially for those who are new to + /// Rust. + /// + /// ### Known problems + /// Rust programmers and learning resources tend to use single + /// character lifetimes, so this lint is at odds with the + /// ecosystem at large. In addition, the lifetime's purpose may + /// be obvious or, rarely, expressible in one character. + /// + /// ### Example + /// ```rust + /// struct DiagnosticCtx<'a> { + /// source: &'a str, + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct DiagnosticCtx<'src> { + /// source: &'src str, + /// } + /// ``` + #[clippy::version = "1.59.0"] + pub SINGLE_CHAR_LIFETIME_NAMES, + restriction, + "warns against single-character lifetime names" +} + +declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); + +impl EarlyLintPass for SingleCharLifetimeNames { + fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { + if in_external_macro(ctx.sess, param.ident.span) { + return; + } + + if let GenericParamKind::Lifetime = param.kind { + if !param.is_placeholder && param.ident.as_str().len() <= 2 { + span_lint_and_help( + ctx, + SINGLE_CHAR_LIFETIME_NAMES, + param.ident.span, + "single-character lifetime names are likely uninformative", + None, + "use a more informative name", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index df1e85afdd799..9b195f3c0a228 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { match expr.kind { ExprKind::Call(count_func, _func_args) => { if_chain! { @@ -64,7 +64,10 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) } } -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { +fn get_pointee_ty_and_count_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { const FUNCTIONS: [&[&str]; 8] = [ &paths::PTR_COPY_NONOVERLAPPING, &paths::PTR_COPY, diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index ad8e72ad764e1..b4a71aefd437f 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -381,7 +381,7 @@ declare_clippy_lint! { declare_lint_pass!(StrToString => [STR_TO_STRING]); -impl LateLintPass<'_> for StrToString { +impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; @@ -431,7 +431,7 @@ declare_clippy_lint! { declare_lint_pass!(StringToString => [STRING_TO_STRING]); -impl LateLintPass<'_> for StringToString { +impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index fee01fb0bd186..d6e948a75607b 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); -impl LateLintPass<'tcx> for StrlenOnCStrings { +impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index faf43fd9fc1ad..ca725918e873e 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -355,7 +355,7 @@ struct BinaryOp<'exprs> { right: &'exprs Expr, } -impl BinaryOp<'exprs> { +impl<'exprs> BinaryOp<'exprs> { fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { Self { op, span, left, right } } @@ -419,7 +419,7 @@ fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { } } -fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> { +fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> { match (&left_outer.kind, &right_outer.kind) { ( ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs index af36f7267004d..c9b2ce476e89d 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -53,13 +53,12 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } } -fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { +fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if_chain! { // First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind; if let Some(last_field) = data.fields().last(); - if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; - if let rustc_hir::ArrayLen::Body(length) = length; + if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind; // Then check if that that array zero-sized let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index fb4abceac25e2..6369aafe3f973 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,11 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::SpanlessHash; +use clippy_utils::{SpanlessEq, SpanlessHash}; +use core::hash::{Hash, Hasher}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; -use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; +use rustc_hir::def::Res; +use rustc_hir::{ + GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, + WherePredicate, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -83,6 +88,53 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { self.check_type_repetition(cx, gen); check_trait_bound_duplication(cx, gen); } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { + let Generics { where_clause, .. } = &item.generics; + let mut self_bounds_set = FxHashSet::default(); + + for predicate in where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !bound_predicate.span.from_expansion(); + if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; + if let Some(PathSegment { res: Some(Res::SelfTy(Some(def_id), _)), .. }) = segments.first(); + + if let Some( + Node::Item( + Item { + kind: ItemKind::Trait(_, _, _, self_bounds, _), + .. } + ) + ) = cx.tcx.hir().get_if_local(*def_id); + then { + if self_bounds_set.is_empty() { + for bound in self_bounds.iter() { + let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue }; + self_bounds_set.insert(self_res); + } + } + + bound_predicate + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .for_each(|(trait_item_res, span)| { + if self_bounds_set.get(&trait_item_res).is_some() { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); + } + }); + } + } + } + } } fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { @@ -94,24 +146,40 @@ fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span) } impl TraitBounds { - fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + struct SpanlessTy<'cx, 'tcx> { + ty: &'tcx Ty<'tcx>, + cx: &'cx LateContext<'tcx>, + } + impl PartialEq for SpanlessTy<'_, '_> { + fn eq(&self, other: &Self) -> bool { + let mut eq = SpanlessEq::new(self.cx); + eq.inter_expr().eq_ty(self.ty, other.ty) + } + } + impl Hash for SpanlessTy<'_, '_> { + fn hash<H: Hasher>(&self, h: &mut H) { + let mut t = SpanlessHash::new(self.cx); + t.hash_ty(self.ty); + h.write_u64(t.finish()); + } + } + impl Eq for SpanlessTy<'_, '_> {} + if gen.span.from_expansion() { return; } - let hash = |ty| -> u64 { - let mut hasher = SpanlessHash::new(cx); - hasher.hash_ty(ty); - hasher.finish() - }; - let mut map: UnhashMap<u64, Vec<&GenericBound<'_>>> = UnhashMap::default(); + let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; if !p.span.from_expansion(); - let h = hash(p.bounded_ty); - if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()); + if let Some(ref v) = map.insert( + SpanlessTy { ty: p.bounded_ty, cx }, + p.bounds.iter().collect::<Vec<_>>() + ); then { let mut hint_string = format!( diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 481e595743585..9d57505e55ed6 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -167,8 +167,9 @@ declare_clippy_lint! { /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// ### Why is this bad? - /// Any `&Box<T>` can also be a `&T`, which is more - /// general. + /// A `&Box<T>` parameter requires the function caller to box `T` first before passing it to a function. + /// Using `&T` defines a concrete type for the parameter and generalizes the function, this would also + /// auto-deref to `&T` at the function call site if passed a `&Box<T>`. /// /// ### Example /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 3d3b4a6679dd1..697ed267e2f35 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -113,8 +113,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } -impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks { - type Map = Map<'hir>; +impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks { + type Map = Map<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None diff --git a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs index c58fa67a02388..7557e14d11f52 100644 --- a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs +++ b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); -impl LateLintPass<'tcx> for UndroppedManuallyDrops { +impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) { let ty = cx.typeck_results().expr_ty(arg_0); diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 46cc76b150e4a..2ffaf24f942ae 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec { } } -fn handle_uninit_vec_pair( +fn handle_uninit_vec_pair<'tcx>( cx: &LateContext<'tcx>, maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, @@ -196,7 +196,7 @@ fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_> } /// Returns self if the expression is `Vec::set_len()` -fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { +fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { // peel unsafe blocks in `unsafe { vec.set_len() }` let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Block(block, _) = e.kind { diff --git a/src/tools/clippy/clippy_lints/src/unit_hash.rs b/src/tools/clippy/clippy_lints/src/unit_hash.rs index 26b4e0f58a870..dcf8a9d7c84d3 100644 --- a/src/tools/clippy/clippy_lints/src/unit_hash.rs +++ b/src/tools/clippy/clippy_lints/src/unit_hash.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { } declare_lint_pass!(UnitHash => [UNIT_HASH]); -impl LateLintPass<'tcx> for UnitHash { +impl<'tcx> LateLintPass<'tcx> for UnitHash { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs index 6d9aff474214c..1dd8895ebd076 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,35 +1,29 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { - let result = match symbol.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", - _ => return, - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "`{}` of unit values detected. This will always {}", - symbol.as_str(), - result - ), - ); - } - } + if let Some(macro_call) = root_macro_call_first_node(cx, expr) { + let macro_name = cx.tcx.item_name(macro_call.def_id); + let result = match macro_name.as_str() { + "assert_eq" | "debug_assert_eq" => "succeed", + "assert_ne" | "debug_assert_ne" => "fail", + _ => return, + }; + let Some ((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !cx.typeck_results().expr_ty(left).is_unit() { + return; } + span_lint( + cx, + UNIT_CMP, + macro_call.span, + &format!("`{}` of unit values detected. This will always {}", macro_name, result), + ); } return; } diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 004530db0861d..287ac5b4a9083 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::{is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -17,10 +17,17 @@ declare_clippy_lint! { /// partial-write/read, use /// `write_all`/`read_exact` instead. /// + /// When working with asynchronous code (either with the `futures` + /// crate or with `tokio`), a similar issue exists for + /// `AsyncWriteExt::write()` and `AsyncReadExt::read()` : these + /// functions are also not guaranteed to process the entire + /// buffer. Your code should either handle partial-writes/reads, or + /// call the `write_all`/`read_exact` methods on those traits instead. + /// /// ### Known problems /// Detects only common patterns. /// - /// ### Example + /// ### Examples /// ```rust,ignore /// use std::io; /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> { @@ -68,6 +75,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } } +/// If `expr` is an (e).await, return the inner expression "e" that's being +/// waited on. Otherwise return None. +fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> { + if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { + if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { + if matches!( + func.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ) { + return Some(arg_0); + } + } + } + + None +} + fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { let mut call = call; while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind { @@ -77,30 +101,69 @@ fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr< break; } } - check_method_call(cx, call, expr); + + if let Some(call) = try_remove_await(call) { + check_method_call(cx, call, expr, true); + } else { + check_method_call(cx, call, expr, false); + } } -fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { +fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) { if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = path.ident.as_str(); - let read_trait = match_trait_method(cx, call, &paths::IO_READ); - let write_trait = match_trait_method(cx, call, &paths::IO_WRITE); + let read_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) + } else { + match_trait_method(cx, call, &paths::IO_READ) + }; + let write_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) + } else { + match_trait_method(cx, call, &paths::IO_WRITE) + }; - match (read_trait, write_trait, symbol) { - (true, _, "read") => span_lint( + match (read_trait, write_trait, symbol, is_await) { + (true, _, "read", false) => span_lint_and_help( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "read amount is not handled", + None, + "use `Read::read_exact` instead, or handle partial reads", + ), + (true, _, "read", true) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "read amount is not handled. Use `Read::read_exact` instead", + "read amount is not handled", + None, + "use `AsyncReadExt::read_exact` instead, or handle partial reads", ), - (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"), - (_, true, "write") => span_lint( + (true, _, "read_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"); + }, + (_, true, "write", false) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "written amount is not handled. Use `Write::write_all` instead", + "written amount is not handled", + None, + "use `Write::write_all` instead, or handle partial writes", ), - (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"), + (_, true, "write", true) => span_lint_and_help( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "written amount is not handled", + None, + "use `AsyncWriteExt::write_all` instead, or handle partial writes", + ), + (_, true, "write_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"); + }, _ => (), } } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 9b06ca4e82493..7751c593e435a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -573,7 +573,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, anon_const); out!("if let ArrayLen::Body({anon_const}) = {length};"); self.body(field!(anon_const.body)); - } + }, } }, ExprKind::Err => kind!("Err"), diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 9c83d30eb9cc1..d6deb50cc9073 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -23,6 +23,14 @@ pub enum DisallowedMethod { WithReason { path: String, reason: Option<String> }, } +impl DisallowedMethod { + pub fn path(&self) -> &str { + let (Self::Simple(path) | Self::WithReason { path, .. }) = self; + + path + } +} + /// A single disallowed type, used by the `DISALLOWED_TYPES` lint. #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] @@ -113,7 +121,7 @@ macro_rules! define_Conf { } } - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] pub mod metadata { use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs index c96766e56784f..e90b6b73b3420 100644 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -342,8 +342,8 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { match length { hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind), hir::ArrayLen::Body(anon_const) => { - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1) - } + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, } }, hir::ExprKind::Err => { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 7d196af7a53f4..9c3dcc8e96a06 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ @@ -34,7 +35,7 @@ use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub mod metadata_collector; declare_clippy_lint! { @@ -410,9 +411,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } self.declared_lints.insert(item.ident.name, item.span); } - } else if is_expn_of(item.span, "impl_lint_pass").is_some() - || is_expn_of(item.span, "declare_lint_pass").is_some() - { + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } if let hir::ItemKind::Impl(hir::Impl { of_trait: None, items: impl_item_refs, @@ -924,9 +929,20 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { let lang_item_path = cx.get_def_path(*item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { - for child in cx.tcx.module_children(*item_def_id) { - if child.ident.name == *item { - return true; + if matches!( + cx.tcx.def_kind(*item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(*item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 7707eebd6223e..4e46d79dc0870 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1,8 +1,7 @@ //! This lint is used to collect metadata about clippy lints. This metadata is exported as a json //! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) //! -//! This module and therefor the entire lint is guarded by a feature flag called -//! `metadata-collector-lint` +//! This module and therefore the entire lint is guarded by a feature flag called `internal` //! //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such @@ -578,7 +577,7 @@ fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { fn get_lint_group_and_level_or_lint( cx: &LateContext<'_>, lint_name: &str, - item: &'hir Item<'_>, + item: &Item<'_>, ) -> Option<(String, &'static str)> { let result = cx .lint_store @@ -697,20 +696,20 @@ fn extract_emission_info<'hir>( } /// Resolves the possible lints that this expression could reference -fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> { +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> { let mut resolver = LintResolver::new(cx); resolver.visit_expr(expr); resolver.lints } /// This function tries to resolve the linked applicability to the given expression. -fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> { +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> { let mut resolver = ApplicabilityResolver::new(cx); resolver.visit_expr(expr); resolver.complete() } -fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind { let mut scanner = IsMultiSpanScanner::new(cx); intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id)); @@ -825,7 +824,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } /// This returns the parent local node if the expression is a reference one -fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { if let hir::def::Res::Local(local_hir) = path.res { return get_parent_local_hir_id(cx, local_hir); @@ -835,7 +834,7 @@ fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Opti None } -fn get_parent_local_hir_id(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); match map.find(map.get_parent_node(hir_id)) { diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index b67448e3a5740..dc385ebacba66 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; pub mod inspector; -#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] +#[cfg(feature = "internal")] pub mod internal_lints; diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index 1bc0eb6303c01..43474da3450e2 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -77,7 +77,7 @@ impl VecPushSearcher { } } -impl LateLintPass<'_> for VecInitThenPush { +impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { self.searcher = None; } diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 0ba0b59ed13da..afff6491aba64 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,17 +1,17 @@ [package] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" edition = "2021" publish = false [dependencies] +arrayvec = { version = "0.7", default-features = false } if_chain = "1.0" rustc-semver = "1.1" [features] deny-warnings = [] -internal-lints = [] -metadata-collector-lint = [] +internal = [] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 8400bfbbd99d2..3d3180521ab7a 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -5,7 +5,6 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::{both, over}; -use if_chain::if_chain; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; @@ -679,34 +678,3 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { _ => false, } } - -/// Extract args from an assert-like macro. -/// -/// Currently working with: -/// - `assert_eq!` and `assert_ne!` -/// - `debug_assert_eq!` and `debug_assert_ne!` -/// -/// For example: -/// -/// `debug_assert_eq!(a, b)` will return Some([a, b]) -pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> { - if_chain! { - if let ExprKind::If(_, ref block, _) = expr.kind; - if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind; - then { - expr = e; - } - } - if_chain! { - if let ExprKind::Block(ref block, _) = expr.kind; - if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind; - if let ExprKind::Match(ref match_expr, _) = expr.kind; - if let ExprKind::Tup(ref tup) = match_expr.kind; - if let [a, b, ..] = tup.as_slice(); - if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind); - then { - return Some([&*a, &*b]); - } - } - None -} diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 5c024612f8eba..34c5af848a6db 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -223,7 +223,7 @@ pub fn constant_simple<'tcx>( constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) } -pub fn constant_full_int( +pub fn constant_full_int<'tcx>( lcx: &LateContext<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, e: &Expr<'_>, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index d47b002ad7aca..ca222c3d66995 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -198,7 +198,7 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))] +#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg<'a, T: LintContext>( cx: &'a T, lint: &'static Lint, diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 61e529a6079c9..c3936ec95d46d 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -45,7 +45,12 @@ impl ops::BitOrAssign for EagernessSuggestion { } /// Determine the eagerness of the given function call. -fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx [Expr<'_>]) -> EagernessSuggestion { +fn fn_eagerness<'tcx>( + cx: &LateContext<'tcx>, + fn_id: DefId, + name: Symbol, + args: &'tcx [Expr<'_>], +) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; let name = name.as_str(); @@ -92,7 +97,7 @@ fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx } #[allow(clippy::too_many_lines)] -fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { +fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, eagerness: EagernessSuggestion, @@ -225,11 +230,11 @@ fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggest } /// Whether the given expression should be changed to evaluate eagerly -pub fn switch_to_eager_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +pub fn switch_to_eager_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { expr_eagerness(cx, expr) == EagernessSuggestion::Eager } /// Whether the given expression should be changed to evaluate lazily -pub fn switch_to_lazy_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +pub fn switch_to_lazy_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { expr_eagerness(cx, expr) == EagernessSuggestion::Lazy } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index c764c35d444fb..160a51740cd7c 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -3,15 +3,13 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::ty::is_type_diagnostic_item; -use crate::{is_expn_of, last_path_segment, match_def_path, paths}; +use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{ - Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp, -}; +use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, symbol, ExpnKind, Span, Symbol}; +use rustc_span::{sym, symbol, Span}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. @@ -428,293 +426,6 @@ pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind { } } -/// Extract args from an assert-like macro. -/// Currently working with: -/// - `assert!`, `assert_eq!` and `assert_ne!` -/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` -/// For example: -/// `assert!(expr)` will return `Some([expr])` -/// `debug_assert_eq!(a, b)` will return `Some([a, b])` -pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> { - /// Try to match the AST for a pattern that contains a match, for example when two args are - /// compared - fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> { - if_chain! { - if let ExprKind::Match(headerexpr, _, _) = &matchblock_expr.kind; - if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind; - if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind; - if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind; - then { - return Some(vec![lhs, rhs]); - } - } - None - } - - if let ExprKind::Block(block, _) = e.kind { - if block.stmts.len() == 1 { - if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind { - // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`) - if_chain! { - if let Some(If { cond, .. }) = If::hir(matchexpr); - if let ExprKind::Unary(UnOp::Not, condition) = cond.kind; - then { - return Some(vec![condition]); - } - } - - // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) - if_chain! { - if let ExprKind::Block(matchblock,_) = matchexpr.kind; - if let Some(matchblock_expr) = matchblock.expr; - then { - return ast_matchblock(matchblock_expr); - } - } - } - } else if let Some(matchblock_expr) = block.expr { - // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) - return ast_matchblock(matchblock_expr); - } - } - None -} - -/// A parsed `format!` expansion -pub struct FormatExpn<'tcx> { - /// Span of `format!(..)` - pub call_site: Span, - /// Inner `format_args!` expansion - pub format_args: FormatArgsExpn<'tcx>, -} - -impl FormatExpn<'tcx> { - /// Parses an expanded `format!` invocation - pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { - if_chain! { - if let ExprKind::Block(block, _) = expr.kind; - if let [stmt] = block.stmts; - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(_, [format_args]) = init.kind; - let expn_data = expr.span.ctxt().outer_expn_data(); - if let ExpnKind::Macro(_, sym::format) = expn_data.kind; - if let Some(format_args) = FormatArgsExpn::parse(format_args); - then { - Some(FormatExpn { - call_site: expn_data.call_site, - format_args, - }) - } else { - None - } - } - } -} - -/// A parsed `format_args!` expansion -pub struct FormatArgsExpn<'tcx> { - /// Span of the first argument, the format string - pub format_string_span: Span, - /// Values passed after the format string - pub value_args: Vec<&'tcx Expr<'tcx>>, - - /// String literal expressions which represent the format string split by "{}" - pub format_string_parts: &'tcx [Expr<'tcx>], - /// Symbols corresponding to [`Self::format_string_parts`] - pub format_string_symbols: Vec<Symbol>, - /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)` - pub args: &'tcx [Expr<'tcx>], - /// The final argument passed to `Arguments::new_v1_formatted`, if applicable - pub fmt_expr: Option<&'tcx Expr<'tcx>>, -} - -impl FormatArgsExpn<'tcx> { - /// Parses an expanded `format_args!` or `format_args_nl!` invocation - pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { - if_chain! { - if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind; - let name = name.as_str(); - if name.ends_with("format_args") || name.ends_with("format_args_nl"); - if let ExprKind::Call(_, args) = expr.kind; - if let Some((strs_ref, args, fmt_expr)) = match args { - // Arguments::new_v1 - [strs_ref, args] => Some((strs_ref, args, None)), - // Arguments::new_v1_formatted - [strs_ref, args, fmt_expr, _unsafe_arg] => Some((strs_ref, args, Some(fmt_expr))), - _ => None, - }; - if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind; - if let ExprKind::Array(format_string_parts) = strs_arr.kind; - if let Some(format_string_symbols) = format_string_parts - .iter() - .map(|e| { - if let ExprKind::Lit(lit) = &e.kind { - if let LitKind::Str(symbol, _style) = lit.node { - return Some(symbol); - } - } - None - }) - .collect(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind; - if let ExprKind::Match(args, [arm], _) = args.kind; - if let ExprKind::Tup(value_args) = args.kind; - if let Some(value_args) = value_args - .iter() - .map(|e| match e.kind { - ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }) - .collect(); - if let ExprKind::Array(args) = arm.body.kind; - then { - Some(FormatArgsExpn { - format_string_span: strs_ref.span, - value_args, - format_string_parts, - format_string_symbols, - args, - fmt_expr, - }) - } else { - None - } - } - } - - /// Returns a vector of `FormatArgsArg`. - pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> { - if let Some(expr) = self.fmt_expr { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; - if let ExprKind::Array(exprs) = expr.kind; - then { - exprs.iter().map(|fmt| { - if_chain! { - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = fmt.kind; - if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); - if let ExprKind::Lit(lit) = &position_field.expr.kind; - if let LitKind::Int(position, _) = lit.node; - if let Ok(i) = usize::try_from(position); - let arg = &self.args[i]; - if let ExprKind::Call(_, [arg_name, _]) = arg.kind; - if let ExprKind::Field(_, j) = arg_name.kind; - if let Ok(j) = j.name.as_str().parse::<usize>(); - then { - Some(FormatArgsArg { value: self.value_args[j], arg, fmt: Some(fmt) }) - } else { - None - } - } - }).collect() - } else { - None - } - } - } else { - Some( - self.value_args - .iter() - .zip(self.args.iter()) - .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None }) - .collect(), - ) - } - } -} - -/// Type representing a `FormatArgsExpn`'s format arguments -pub struct FormatArgsArg<'tcx> { - /// An element of `value_args` according to `position` - pub value: &'tcx Expr<'tcx>, - /// An element of `args` according to `position` - pub arg: &'tcx Expr<'tcx>, - /// An element of `fmt_expn` - pub fmt: Option<&'tcx Expr<'tcx>>, -} - -impl<'tcx> FormatArgsArg<'tcx> { - /// Returns true if any formatting parameters are used that would have an effect on strings, - /// like `{:+2}` instead of just `{}`. - pub fn has_string_formatting(&self) -> bool { - self.fmt.map_or(false, |fmt| { - // `!` because these conditions check that `self` is unformatted. - !if_chain! { - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = fmt.kind; - if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); - // struct `core::fmt::rt::v1::FormatSpec` - if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind; - let mut precision_found = false; - let mut width_found = false; - if subfields.iter().all(|field| { - match field.ident.name { - sym::precision => { - precision_found = true; - if let ExprKind::Path(ref precision_path) = field.expr.kind { - last_path_segment(precision_path).ident.name == sym::Implied - } else { - false - } - } - sym::width => { - width_found = true; - if let ExprKind::Path(ref width_qpath) = field.expr.kind { - last_path_segment(width_qpath).ident.name == sym::Implied - } else { - false - } - } - _ => true, - } - }); - if precision_found && width_found; - then { true } else { false } - } - }) - } - - /// Returns true if the argument is formatted using `Display::fmt`. - pub fn is_display(&self) -> bool { - if_chain! { - if let ExprKind::Call(_, [_, format_field]) = self.arg.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind; - if let [.., t, _] = path.segments; - if t.ident.name == sym::Display; - then { true } else { false } - } - } -} - -/// A parsed `panic!` expansion -pub struct PanicExpn<'tcx> { - /// Span of `panic!(..)` - pub call_site: Span, - /// Inner `format_args!` expansion - pub format_args: FormatArgsExpn<'tcx>, -} - -impl PanicExpn<'tcx> { - /// Parses an expanded `panic!` invocation - pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { - if_chain! { - if let ExprKind::Call(_, [format_args]) = expr.kind; - let expn_data = expr.span.ctxt().outer_expn_data(); - if let Some(format_args) = FormatArgsExpn::parse(format_args); - then { - Some(PanicExpn { - call_site: expn_data.call_site, - format_args, - }) - } else { - None - } - } - } -} - /// A parsed `Vec` initialization expression #[derive(Clone, Copy)] pub enum VecInitKind { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index ac2b1a0259e3e..5a08a411dd13f 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; use rustc_hir::HirIdMap; use rustc_hir::{ - BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, + ArrayLen, BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt, - StmtKind, Ty, TyKind, TypeBinding, ArrayLen + StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -171,11 +171,11 @@ impl HirEqInterExpr<'_, '_, '_> { } pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool { - match (left, right) { - (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true, - (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body), - (_, _) => false, - } + match (left, right) { + (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true, + (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body), + (_, _) => false, + } } pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool { @@ -396,12 +396,10 @@ impl HirEqInterExpr<'_, '_, '_> { } #[allow(clippy::similar_names)] - fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { + pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), - (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => { - self.eq_ty(lt, rt) && self.eq_array_length(ll, rl) - }, + (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) }, @@ -853,6 +851,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_path(&mut self, path: &Path<'_>) { match path.res { // constant hash since equality is dependant on inter-expression context + // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal + // even though the binding names are different and they have different `HirId`s. Res::Local(_) => 1_usize.hash(&mut self.s), _ => { for seg in path.segments { @@ -963,7 +963,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_array_length(&mut self, length: ArrayLen) { match length { - ArrayLen::Infer(..) => {} + ArrayLen::Infer(..) => {}, ArrayLen::Body(anon_const) => self.hash_body(anon_const.body), } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 90f5baffd22fc..57183b58b2a11 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,6 +1,5 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(in_band_lifetimes)] #![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] @@ -44,6 +43,7 @@ pub mod diagnostics; pub mod eager_or_lazy; pub mod higher; mod hir_utils; +pub mod macros; pub mod msrvs; pub mod numeric_literal; pub mod paths; @@ -70,16 +70,16 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, - ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, - MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, - TraitItemKind, TraitRef, TyKind, UnOp, ArrayLen + def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, + ExprKind, FnDecl, ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, + Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + Target, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; @@ -126,7 +126,7 @@ macro_rules! extract_msrv_attr { extract_msrv_attr!(@EarlyContext); }; (@$context:ident$(, $call:tt)?) => { - fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { use $crate::get_unique_inner_attr; match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") { Some(msrv_attr) => { @@ -146,13 +146,6 @@ macro_rules! extract_msrv_attr { }; } -/// Returns `true` if the span comes from a macro expansion, no matter if from a -/// macro by example or from a procedural macro -#[must_use] -pub fn in_macro(span: Span) -> bool { - span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) -} - /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). #[must_use] @@ -282,7 +275,11 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { } /// Checks if the first type parameter is a lang item. -pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> { +pub fn is_ty_param_lang_item<'tcx>( + cx: &LateContext<'_>, + qpath: &QPath<'tcx>, + item: LangItem, +) -> Option<&'tcx hir::Ty<'tcx>> { let ty = get_qpath_generic_tys(qpath).next()?; if let TyKind::Path(qpath) = &ty.kind { @@ -298,7 +295,7 @@ pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: La } /// Checks if the first type parameter is a diagnostic item. -pub fn is_ty_param_diagnostic_item( +pub fn is_ty_param_diagnostic_item<'tcx>( cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: Symbol, @@ -375,7 +372,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { } } -pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> { +pub fn get_qpath_generics<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> { match path { QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args), QPath::TypeRelative(_, s) => s.args, @@ -383,7 +380,7 @@ pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> } } -pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> { +pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> { get_qpath_generics(path) .map_or([].as_ref(), |a| a.args) .iter() @@ -522,7 +519,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { } }; } - fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<Res> { + fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> { match tcx.def_kind(def_id) { DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx .module_children(def_id) @@ -538,18 +535,34 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { _ => None, } } + fn find_primitive(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> { + if let Some(&(index, Target::Impl)) = lang_items::ITEM_REFS.get(&Symbol::intern(name)) { + tcx.lang_items().items()[index] + } else { + None + } + } + fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> { + tcx.crates(()) + .iter() + .find(|&&num| tcx.crate_name(num).as_str() == name) + .map(CrateNum::as_def_id) + } - let (krate, first, path) = match *path { - [krate, first, ref path @ ..] => (krate, first, path), + let (base, first, path) = match *path { + [base, first, ref path @ ..] => (base, first, path), [primitive] => { return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); }, _ => return Res::Err, }; let tcx = cx.tcx; - let crates = tcx.crates(()); - let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)); - let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first)); + let first = try_res!( + find_primitive(tcx, base) + .or_else(|| find_crate(tcx, base)) + .and_then(|id| item_child_by_name(tcx, id, first)) + ); + let last = path .iter() .copied() @@ -628,6 +641,19 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &' (result, root) } +/// Gets the mutability of the custom deref adjustment, if any. +pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> { + cx.typeck_results() + .expr_adjustments(e) + .iter() + .find_map(|a| match a.kind { + Adjust::Deref(Some(d)) => Some(Some(d.mutbl)), + Adjust::Deref(None) => None, + _ => Some(None), + }) + .and_then(|x| x) +} + /// Checks if two expressions can be mutably borrowed simultaneously /// and they aren't dependent on borrowing same thing twice pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool { @@ -636,7 +662,15 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - if !eq_expr_value(cx, r1, r2) { return true; } + if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() { + return false; + } + for (x1, x2) in s1.iter().zip(s2.iter()) { + if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() { + return false; + } + match (&x1.kind, &x2.kind) { (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => { if i1 != i2 { @@ -710,8 +744,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { _ => false, }, ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), - ExprKind::Repeat(x, len) => if_chain! { - if let ArrayLen::Body(len) = len; + ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! { if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind; if let LitKind::Int(v, _) = const_lit.node; if v <= 32 && is_default_equivalent(cx, x); @@ -760,7 +793,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// /// Note that this check is not recursive, so passing the `if` expression will always return true /// even though sub-expressions might return false. -pub fn can_move_expr_to_closure_no_visit( +pub fn can_move_expr_to_closure_no_visit<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_ids: &[HirId], @@ -835,7 +868,7 @@ impl std::ops::BitOrAssign for CaptureKind { /// Note as this will walk up to parent expressions until the capture can be determined it should /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or /// function argument (other than a receiver). -pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { +pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { let mut capture = CaptureKind::Ref(Mutability::Not); pat.each_binding_or_first(&mut |_, id, span, _| match cx @@ -935,7 +968,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind /// Checks if the expression can be moved into a closure as is. This will return a list of captures /// if so, otherwise, `None`. -pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> { +pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, // Stack of potential break targets contained in the expression. @@ -948,7 +981,7 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> /// mutable reference. captures: HirIdMap<CaptureKind>, } - impl Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -1146,19 +1179,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { found } -/// Finds calls of the specified macros in a function body. -pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> { - let mut result = Vec::new(); - expr_visitor_no_bodies(|expr| { - if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) { - result.push(expr.span); - } - true - }) - .visit_expr(&body.value); - result -} - /// Extends the span to the beginning of the spans line, incl. whitespaces. /// /// ```rust @@ -1218,7 +1238,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio } /// Gets the loop or closure enclosing the given expression, if any. -pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { for (_, node) in tcx.hir().parent_iter(expr.hir_id) { match node { Node::Expr( @@ -1687,32 +1707,6 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name) } -pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Call(func, [arg]) = expr.kind { - expr_path_res(cx, func) - .opt_def_id() - .map_or(false, |id| match_panic_def_id(cx, id)) - .then(|| arg) - } else { - None - } -} - -pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { - match_any_def_paths( - cx, - did, - &[ - &paths::BEGIN_PANIC, - &paths::PANIC_ANY, - &paths::PANICKING_PANIC, - &paths::PANICKING_PANIC_FMT, - &paths::PANICKING_PANIC_STR, - ], - ) - .is_some() -} - /// Returns the list of condition expressions and the list of blocks in a /// sequence of `if/else`. /// E.g., this returns `([a, b], [c, d, e])` for the expression @@ -1752,7 +1746,7 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool { } /// Peels away all the compiler generated code surrounding the body of an async function, -pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Call( _, &[ @@ -1856,7 +1850,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } /// Gets the node where an expression is either used, or it's type is unified with another branch. -pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> { +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> { let mut child_id = expr.hir_id; let mut iter = tcx.hir().parent_iter(child_id); loop { @@ -2062,8 +2056,8 @@ where /// Peels off all references on the pattern. Returns the underlying pattern and the number of /// references removed. -pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { - fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { +pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { + fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { if let PatKind::Ref(pat, _) = pat.kind { peel(pat, count + 1) } else { @@ -2086,7 +2080,7 @@ pub fn peel_hir_expr_while<'tcx>( /// Peels off up to the given number of references on the expression. Returns the underlying /// expression and the number of references removed. -pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { +pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { let mut remaining = count; let e = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => { @@ -2100,7 +2094,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, /// Peels off all references on the expression. Returns the underlying expression and the number of /// references removed. -pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { +pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { let mut count = 0; let e = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => { @@ -2183,7 +2177,7 @@ impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> { static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new(); -fn with_test_item_names(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { +fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap(); match map.entry(module) { diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs new file mode 100644 index 0000000000000..b7a242cf90a43 --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -0,0 +1,539 @@ +#![allow(clippy::similar_names)] // `expr` and `expn` + +use crate::visitors::expr_visitor_no_bodies; + +use arrayvec::ArrayVec; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; +use rustc_lint::LateContext; +use rustc_span::def_id::DefId; +use rustc_span::hygiene::{MacroKind, SyntaxContext}; +use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol}; +use std::ops::ControlFlow; + +/// A macro call, like `vec![1, 2, 3]`. +/// +/// Use `tcx.item_name(macro_call.def_id)` to get the macro name. +/// Even better is to check if it is a diagnostic item. +/// +/// This structure is similar to `ExpnData` but it precludes desugaring expansions. +#[derive(Debug)] +pub struct MacroCall { + /// Macro `DefId` + pub def_id: DefId, + /// Kind of macro + pub kind: MacroKind, + /// The expansion produced by the macro call + pub expn: ExpnId, + /// Span of the macro call site + pub span: Span, +} + +impl MacroCall { + pub fn is_local(&self) -> bool { + span_is_local(self.span) + } +} + +/// Returns an iterator of expansions that created the given span +pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> { + std::iter::from_fn(move || { + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() { + return None; + } + let expn = ctxt.outer_expn(); + let data = expn.expn_data(); + span = data.call_site; + Some((expn, data)) + }) +} + +/// Checks whether the span is from the root expansion or a locally defined macro +pub fn span_is_local(span: Span) -> bool { + !span.from_expansion() || expn_is_local(span.ctxt().outer_expn()) +} + +/// Checks whether the expansion is the root expansion or a locally defined macro +pub fn expn_is_local(expn: ExpnId) -> bool { + if expn == ExpnId::root() { + return true; + } + let data = expn.expn_data(); + let backtrace = expn_backtrace(data.call_site); + std::iter::once((expn, data)) + .chain(backtrace) + .find_map(|(_, data)| data.macro_def_id) + .map_or(true, DefId::is_local) +} + +/// Returns an iterator of macro expansions that created the given span. +/// Note that desugaring expansions are skipped. +pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> { + expn_backtrace(span).filter_map(|(expn, data)| match data { + ExpnData { + kind: ExpnKind::Macro(kind, _), + macro_def_id: Some(def_id), + call_site: span, + .. + } => Some(MacroCall { + def_id, + kind, + expn, + span, + }), + _ => None, + }) +} + +/// If the macro backtrace of `span` has a macro call at the root expansion +/// (i.e. not a nested macro call), returns `Some` with the `MacroCall` +pub fn root_macro_call(span: Span) -> Option<MacroCall> { + macro_backtrace(span).last() +} + +/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node" +/// produced by the macro call, as in [`first_node_in_macro`]. +pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> { + if first_node_in_macro(cx, node) != Some(ExpnId::root()) { + return None; + } + root_macro_call(node.span()) +} + +/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the +/// macro call, as in [`first_node_in_macro`]. +pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> { + let span = node.span(); + first_node_in_macro(cx, node) + .into_iter() + .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn)) +} + +/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the +/// macro call site (i.e. the parent of the macro expansion). This generally means that `node` +/// is the outermost node of an entire macro expansion, but there are some caveats noted below. +/// This is useful for finding macro calls while visiting the HIR without processing the macro call +/// at every node within its expansion. +/// +/// If you already have immediate access to the parent node, it is simpler to +/// just check the context of that span directly (e.g. `parent.span.from_expansion()`). +/// +/// If a macro call is in statement position, it expands to one or more statements. +/// In that case, each statement *and* their immediate descendants will all yield `Some` +/// with the `ExpnId` of the containing block. +/// +/// A node may be the "first node" of multiple macro calls in a macro backtrace. +/// The expansion of the outermost macro call site is returned in such cases. +pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> { + // get the macro expansion or return `None` if not found + // `macro_backtrace` importantly ignores desugaring expansions + let expn = macro_backtrace(node.span()).next()?.expn; + + // get the parent node, possibly skipping over a statement + // if the parent is not found, it is sensible to return `Some(root)` + let hir = cx.tcx.hir(); + let mut parent_iter = hir.parent_iter(node.hir_id()); + let (parent_id, _) = match parent_iter.next() { + None => return Some(ExpnId::root()), + Some((_, Node::Stmt(_))) => match parent_iter.next() { + None => return Some(ExpnId::root()), + Some(next) => next, + }, + Some(next) => next, + }; + + // get the macro expansion of the parent node + let parent_span = hir.span(parent_id); + let Some(parent_macro_call) = macro_backtrace(parent_span).next() else { + // the parent node is not in a macro + return Some(ExpnId::root()); + }; + + if parent_macro_call.expn.is_descendant_of(expn) { + // `node` is input to a macro call + return None; + } + + Some(parent_macro_call.expn) +} + +/* Specific Macro Utils */ + +/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros +pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { + let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; + matches!( + name.as_str(), + "core_panic_macro" + | "std_panic_macro" + | "core_panic_2015_macro" + | "std_panic_2015_macro" + | "core_panic_2021_macro" + ) +} + +pub enum PanicExpn<'a> { + /// No arguments - `panic!()` + Empty, + /// A string literal or any `&str` - `panic!("message")` or `panic!(message)` + Str(&'a Expr<'a>), + /// A single argument that implements `Display` - `panic!("{}", object)` + Display(&'a Expr<'a>), + /// Anything else - `panic!("error {}: {}", a, b)` + Format(FormatArgsExpn<'a>), +} + +impl<'a> PanicExpn<'a> { + pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> { + if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) { + return None; + } + let ExprKind::Call(callee, [arg]) = &expr.kind else { return None }; + let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None }; + let result = match path.segments.last().unwrap().ident.as_str() { + "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, + "panic" | "panic_str" => Self::Str(arg), + "panic_display" => { + let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None }; + Self::Display(e) + }, + "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?), + _ => return None, + }; + Some(result) + } +} + +/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion +pub fn find_assert_args<'a>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, +) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> { + find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p)) +} + +/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro +/// expansion +pub fn find_assert_eq_args<'a>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, +) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> { + find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p)) +} + +fn find_assert_args_inner<'a, const N: usize>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, +) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> { + let macro_id = expn.expn_data().macro_def_id?; + let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") { + None => (expr, expn), + Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?, + }; + let mut args = ArrayVec::new(); + let mut panic_expn = None; + expr_visitor_no_bodies(|e| { + if args.is_full() { + if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { + panic_expn = PanicExpn::parse(cx, e); + } + panic_expn.is_none() + } else if is_assert_arg(cx, e, expn) { + args.push(e); + false + } else { + true + } + }) + .visit_expr(expr); + let args = args.into_inner().ok()?; + // if no `panic!(..)` is found, use `PanicExpn::Empty` + // to indicate that the default assertion message is used + let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty); + Some((args, panic_expn)) +} + +fn find_assert_within_debug_assert<'a>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, + assert_name: Symbol, +) -> Option<(&'a Expr<'a>, ExpnId)> { + let mut found = None; + expr_visitor_no_bodies(|e| { + if found.is_some() || !e.span.from_expansion() { + return false; + } + let e_expn = e.span.ctxt().outer_expn(); + if e_expn == expn { + return true; + } + if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { + found = Some((e, e_expn)); + } + false + }) + .visit_expr(expr); + found +} + +fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool { + if !expr.span.from_expansion() { + return true; + } + let result = macro_backtrace(expr.span).try_for_each(|macro_call| { + if macro_call.expn == assert_expn { + ControlFlow::Break(false) + } else { + match cx.tcx.item_name(macro_call.def_id) { + // `cfg!(debug_assertions)` in `debug_assert!` + sym::cfg => ControlFlow::CONTINUE, + // assert!(other_macro!(..)) + _ => ControlFlow::Break(true), + } + } + }); + match result { + ControlFlow::Break(is_assert_arg) => is_assert_arg, + ControlFlow::Continue(()) => true, + } +} + +/// A parsed `format_args!` expansion +pub struct FormatArgsExpn<'tcx> { + /// Span of the first argument, the format string + pub format_string_span: Span, + /// The format string split by formatted args like `{..}` + pub format_string_parts: Vec<Symbol>, + /// Values passed after the format string + pub value_args: Vec<&'tcx Expr<'tcx>>, + /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`) + pub formatters: Vec<(usize, Symbol)>, + /// List of `fmt::v1::Argument { .. }` expressions. If this is empty, + /// then `formatters` represents the format args (`{..}`). + /// If this is non-empty, it represents the format args, and the `position` + /// parameters within the struct expressions are indexes of `formatters`. + pub specs: Vec<&'tcx Expr<'tcx>>, +} + +impl<'tcx> FormatArgsExpn<'tcx> { + /// Parses an expanded `format_args!` or `format_args_nl!` invocation + pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> { + macro_backtrace(expr.span).find(|macro_call| { + matches!( + cx.tcx.item_name(macro_call.def_id), + sym::const_format_args | sym::format_args | sym::format_args_nl + ) + })?; + let mut format_string_span: Option<Span> = None; + let mut format_string_parts: Vec<Symbol> = Vec::new(); + let mut value_args: Vec<&Expr<'_>> = Vec::new(); + let mut formatters: Vec<(usize, Symbol)> = Vec::new(); + let mut specs: Vec<&Expr<'_>> = Vec::new(); + expr_visitor_no_bodies(|e| { + // if we're still inside of the macro definition... + if e.span.ctxt() == expr.span.ctxt() { + // ArgumnetV1::new(<value>, <format_trait>::fmt) + if_chain! { + if let ExprKind::Call(callee, [val, fmt_path]) = e.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind; + if seg.ident.name == sym::new; + if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind; + if path.segments.last().unwrap().ident.name == sym::ArgumentV1; + if let ExprKind::Path(QPath::Resolved(_, path)) = fmt_path.kind; + if let [.., fmt_trait, _fmt] = path.segments; + then { + let val_idx = if_chain! { + if val.span.ctxt() == expr.span.ctxt(); + if let ExprKind::Field(_, field) = val.kind; + if let Ok(idx) = field.name.as_str().parse(); + then { + // tuple index + idx + } else { + // assume the value expression is passed directly + formatters.len() + } + }; + formatters.push((val_idx, fmt_trait.ident.name)); + } + } + if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind { + if path.segments.last().unwrap().ident.name == sym::Argument { + specs.push(e); + } + } + // walk through the macro expansion + return true; + } + // assume that the first expr with a differing context represents + // (and has the span of) the format string + if format_string_span.is_none() { + format_string_span = Some(e.span); + let span = e.span; + // walk the expr and collect string literals which are format string parts + expr_visitor_no_bodies(|e| { + if e.span.ctxt() != span.ctxt() { + // defensive check, probably doesn't happen + return false; + } + if let ExprKind::Lit(lit) = &e.kind { + if let LitKind::Str(symbol, _s) = lit.node { + format_string_parts.push(symbol); + } + } + true + }) + .visit_expr(e); + } else { + // assume that any further exprs with a differing context are value args + value_args.push(e); + } + // don't walk anything not from the macro expansion (e.a. inputs) + false + }) + .visit_expr(expr); + Some(FormatArgsExpn { + format_string_span: format_string_span?, + format_string_parts, + value_args, + formatters, + specs, + }) + } + + /// Finds a nested call to `format_args!` within a `format!`-like macro call + pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> { + let mut format_args = None; + expr_visitor_no_bodies(|e| { + if format_args.is_some() { + return false; + } + let e_ctxt = e.span.ctxt(); + if e_ctxt == expr.span.ctxt() { + return true; + } + if e_ctxt.outer_expn().is_descendant_of(expn_id) { + format_args = FormatArgsExpn::parse(cx, e); + } + false + }) + .visit_expr(expr); + format_args + } + + /// Returns a vector of `FormatArgsArg`. + pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> { + if self.specs.is_empty() { + let args = std::iter::zip(&self.value_args, &self.formatters) + .map(|(value, &(_, format_trait))| FormatArgsArg { + value, + format_trait, + spec: None, + }) + .collect(); + return Some(args); + } + self.specs + .iter() + .map(|spec| { + if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = spec.kind; + if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); + if let ExprKind::Lit(lit) = &position_field.expr.kind; + if let LitKind::Int(position, _) = lit.node; + if let Ok(i) = usize::try_from(position); + if let Some(&(j, format_trait)) = self.formatters.get(i); + then { + Some(FormatArgsArg { value: self.value_args[j], format_trait, spec: Some(spec) }) + } else { + None + } + } + }) + .collect() + } + + /// Span of all inputs + pub fn inputs_span(&self) -> Span { + match *self.value_args { + [] => self.format_string_span, + [.., last] => self.format_string_span.to(last.span), + } + } +} + +/// Type representing a `FormatArgsExpn`'s format arguments +pub struct FormatArgsArg<'tcx> { + /// An element of `value_args` according to `position` + pub value: &'tcx Expr<'tcx>, + /// An element of `args` according to `position` + pub format_trait: Symbol, + /// An element of `specs` + pub spec: Option<&'tcx Expr<'tcx>>, +} + +impl<'tcx> FormatArgsArg<'tcx> { + /// Returns true if any formatting parameters are used that would have an effect on strings, + /// like `{:+2}` instead of just `{}`. + pub fn has_string_formatting(&self) -> bool { + self.spec.map_or(false, |spec| { + // `!` because these conditions check that `self` is unformatted. + !if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = spec.kind; + if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); + // struct `core::fmt::rt::v1::FormatSpec` + if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind; + if subfields.iter().all(|field| match field.ident.name { + sym::precision | sym::width => match field.expr.kind { + ExprKind::Path(QPath::Resolved(_, path)) => { + path.segments.last().unwrap().ident.name == sym::Implied + } + _ => false, + } + _ => true, + }); + then { true } else { false } + } + }) + } +} + +/// A node with a `HirId` and a `Span` +pub trait HirNode { + fn hir_id(&self) -> HirId; + fn span(&self) -> Span; +} + +macro_rules! impl_hir_node { + ($($t:ident),*) => { + $(impl HirNode for hir::$t<'_> { + fn hir_id(&self) -> HirId { + self.hir_id + } + fn span(&self) -> Span { + self.span + } + })* + }; +} + +impl_hir_node!(Expr, Pat); + +impl HirNode for hir::Item<'_> { + fn hir_id(&self) -> HirId { + self.hir_id() + } + + fn span(&self) -> Span { + self.span + } +} diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 0cec7d6a5e402..a5b409ad96bbb 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -12,8 +12,9 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,53,0 { OR_PATTERNS } + 1,53,0 { OR_PATTERNS, MANUAL_BITS } 1,52,0 { STR_SPLIT_ONCE } + 1,51,0 { BORROW_AS_PTR } 1,50,0 { BOOL_THEN } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 6171823abbbd0..288c56e9fd737 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -5,16 +5,16 @@ //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information. pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ ["rustc_lint_defs", "Applicability", "Unspecified"], ["rustc_lint_defs", "Applicability", "HasPlaceholders"], ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], ["rustc_lint_defs", "Applicability", "MachineApplicable"], ]; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros @@ -25,7 +25,6 @@ pub const ASSERT_MACRO: [&str; 4] = ["core", "macros", "builtin", "assert"]; pub const ASSERT_NE_MACRO: [&str; 3] = ["core", "macros", "assert_ne"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; -pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; /// Preferably use the diagnostic item `sym::Borrow` where possible pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"]; @@ -46,7 +45,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros pub const EPRINT_MACRO: [&str; 3] = ["std", "macros", "eprint"]; @@ -64,13 +63,17 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; @@ -82,11 +85,11 @@ pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; -#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] +#[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; @@ -106,10 +109,6 @@ pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; -pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"]; -pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"]; -pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"]; -pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"]; pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; @@ -118,6 +117,7 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWri pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; +#[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; @@ -180,20 +180,24 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 836558b07cb02..8bdc9a9ea1629 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -19,7 +19,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -85,7 +85,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru Ok(()) } -fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { for arg in ty.walk(tcx) { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -133,7 +133,13 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { Ok(()) } -fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult { +fn check_rvalue<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + def_id: DefId, + rvalue: &Rvalue<'tcx>, + span: Span, +) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body), @@ -210,7 +216,12 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv } } -fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult { +fn check_statement<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + def_id: DefId, + statement: &Statement<'tcx>, +) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { @@ -239,7 +250,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen } } -fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { match operand { Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { @@ -249,7 +260,7 @@ fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: & } } -fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); while let [ref proj_base @ .., elem] = *cursor { cursor = proj_base; @@ -274,7 +285,7 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t Ok(()) } -fn check_terminator( +fn check_terminator<'a, 'tcx>( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>, diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index d928317259da0..dbad607c58ea3 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -281,7 +281,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// correctly get a snippet of `vec![]`. /// /// This will also return whether or not the snippet is a macro call. -pub fn snippet_with_context( +pub fn snippet_with_context<'a>( cx: &LateContext<'_>, span: Span, outer: SyntaxContext, diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 92662c59226a2..87bc8232dde3a 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -461,7 +461,7 @@ impl Neg for Sugg<'_> { } } -impl Not for Sugg<'a> { +impl<'a> Not for Sugg<'a> { type Output = Sugg<'a>; fn not(self) -> Sugg<'a> { use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual}; @@ -846,7 +846,7 @@ struct DerefDelegate<'a, 'tcx> { applicability: Applicability, } -impl DerefDelegate<'_, 'tcx> { +impl<'tcx> DerefDelegate<'_, 'tcx> { /// build final suggestion: /// - create the ending part of suggestion /// - concatenate starting and ending parts diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 6d191d4a59bde..72317447159a9 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -25,7 +25,7 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } /// Checks whether a type can be partially moved. -pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { +pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { if has_drop(cx, ty) || is_copy(cx, ty) { return false; } @@ -366,7 +366,7 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { /// Returns `true` if types `a` and `b` are same types having same `Const` generic args, /// otherwise returns `false` -pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { +pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind(), &b.kind()) { (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { if did_a != did_b { diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 4bfd3c64b9c36..b60cd4736f32a 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -173,7 +173,7 @@ pub trait Visitable<'tcx> { } macro_rules! visitable_ref { ($t:ident, $f:ident) => { - impl Visitable<'tcx> for &'tcx $t<'tcx> { + impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { visitor.$f(self); } @@ -217,7 +217,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { } /// Checks if the given local is used. -pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { +pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { let mut is_used = false; let mut visitor = expr_visitor(cx, |expr| { if !is_used { @@ -231,7 +231,7 @@ pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id } /// Checks if the given expression is a constant. -pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, is_const: bool, @@ -321,7 +321,7 @@ pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. -pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, is_unsafe: bool, diff --git a/src/tools/clippy/doc/common_tools_writing_lints.md b/src/tools/clippy/doc/common_tools_writing_lints.md index c7e51d53f511d..207b0be154885 100644 --- a/src/tools/clippy/doc/common_tools_writing_lints.md +++ b/src/tools/clippy/doc/common_tools_writing_lints.md @@ -60,7 +60,7 @@ Two noticeable items here: Starting with an `expr`, you can check whether it is calling a specific method `some_method`: ```rust -impl LateLintPass<'_> for MyStructLint { +impl<'tcx> LateLintPass<'tcx> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { // Check our expr is calling a method diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 53e669254cfea..9af8dcc7726f0 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -665,16 +665,6 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { logs_modified < clippy_modified } -fn is_in_clippy_root() -> bool { - if let Ok(pb) = std::env::current_dir() { - if let Some(file) = pb.file_name() { - return file == PathBuf::from("rust-clippy"); - } - } - - false -} - /// lintchecks `main()` function /// /// # Panics @@ -683,7 +673,7 @@ fn is_in_clippy_root() -> bool { /// or if lintcheck is executed from the wrong directory (aka none-repo-root) pub fn main() { // assert that we launch lintcheck from the repo root (via cargo lintcheck) - if !is_in_clippy_root() { + if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively."); std::process::exit(3); } diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 471ae40f1ac7a..e6a58e9207250 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-30" +channel = "nightly-2022-01-13" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index 7ebdd947893e9..240e233420f0e 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -96,7 +96,7 @@ impl ClippyCmd { clippy_args.push("--no-deps".into()); } - ClippyCmd { + Self { cargo_subcommand, args, clippy_args, diff --git a/src/tools/clippy/tests/cargo/mod.rs b/src/tools/clippy/tests/cargo/mod.rs deleted file mode 100644 index 4dbe71e4b6ad6..0000000000000 --- a/src/tools/clippy/tests/cargo/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[must_use] -pub fn is_rustc_test_suite() -> bool { - option_env!("RUSTC_TEST_SUITE").is_some() -} diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index a2d58491872b3..531890c863f5e 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -1,4 +1,5 @@ #![feature(test)] // compiletest_rs requires this attribute +#![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] @@ -11,16 +12,18 @@ use std::ffi::{OsStr, OsString}; use std::fs; use std::io; use std::path::{Path, PathBuf}; +use test_utils::IS_RUSTC_TEST_SUITE; -mod cargo; +mod test_utils; // whether to run internal tests or not -const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ "clippy_utils", "derive_new", + "futures", "if_chain", "itertools", "quote", @@ -28,6 +31,7 @@ static TEST_DEPENDENCIES: &[&str] = &[ "serde", "serde_derive", "syn", + "tokio", "parking_lot", ]; @@ -38,6 +42,8 @@ extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; #[allow(unused_extern_crates)] +extern crate futures; +#[allow(unused_extern_crates)] extern crate if_chain; #[allow(unused_extern_crates)] extern crate itertools; @@ -47,6 +53,8 @@ extern crate parking_lot; extern crate quote; #[allow(unused_extern_crates)] extern crate syn; +#[allow(unused_extern_crates)] +extern crate tokio; /// Produces a string with an `--extern` flag for all UI test crate /// dependencies. @@ -298,7 +306,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index a37cdfed126f6..67af9d05bf402 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -3,184 +3,31 @@ //! //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context -// Dogfood cannot run on Windows -#![cfg(not(windows))] #![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] -use std::lazy::SyncLazy; use std::path::PathBuf; use std::process::Command; +use test_utils::IS_RUSTC_TEST_SUITE; -mod cargo; - -static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| { - let mut path = std::env::current_exe().unwrap(); - assert!(path.pop()); // deps - path.set_file_name("cargo-clippy"); - path -}); +mod test_utils; #[test] fn dogfood_clippy() { - // run clippy on itself and fail the test if lint warnings are reported - if cargo::is_rustc_test_suite() { - return; - } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - - let mut command = Command::new(&*CLIPPY_PATH); - command - .current_dir(root_dir) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { - command.args(&["-D", "clippy::internal"]); - } - - let output = command.output().unwrap(); - - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); -} - -fn test_no_deps_ignores_path_deps_in_workspaces() { - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } - let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let target_dir = root.join("target").join("dogfood"); - let cwd = root.join("clippy_workspace_tests"); - - // Make sure we start with a clean state - Command::new("cargo") - .current_dir(&cwd) - .env("CARGO_TARGET_DIR", &target_dir) - .arg("clean") - .args(&["-p", "subcrate"]) - .args(&["-p", "path_dep"]) - .output() - .unwrap(); - - // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. - // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--no-deps") - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); - - let lint_path_dep = || { - // Test that without the `--no-deps` argument, `path_dep` is linted. - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(!output.status.success()); - assert!( - String::from_utf8(output.stderr) - .unwrap() - .contains("error: empty `loop {}` wastes CPU cycles") - ); - }; - - // Make sure Cargo is aware of the removal of `--no-deps`. - lint_path_dep(); - - let successful_build = || { - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); - - output - }; - - // Trigger a sucessful build, so Cargo would like to cache the build result. - successful_build(); - - // Make sure there's no spurious rebuild when nothing changes. - let stderr = String::from_utf8(successful_build().stderr).unwrap(); - assert!(!stderr.contains("Compiling")); - assert!(!stderr.contains("Checking")); - assert!(stderr.contains("Finished")); - - // Make sure Cargo is aware of the new `--cfg` flag. - lint_path_dep(); -} -#[test] -fn dogfood_subprojects() { - // run clippy on remaining subprojects and fail the test if lint warnings are reported - if cargo::is_rustc_test_suite() { - return; + // "" is the root package + for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + run_clippy_for_package(package); } - - // NOTE: `path_dep` crate is omitted on purpose here - for project in &[ - "clippy_workspace_tests", - "clippy_workspace_tests/src", - "clippy_workspace_tests/subcrate", - "clippy_workspace_tests/subcrate/src", - "clippy_dev", - "clippy_lints", - "clippy_utils", - "rustc_tools_util", - ] { - run_clippy_for_project(project); - } - - // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the - // same time, so we test this immediately after the dogfood for workspaces. - test_no_deps_ignores_path_deps_in_workspaces(); } #[test] #[ignore] -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] fn run_metadata_collection_lint() { use std::fs::File; use std::time::SystemTime; @@ -191,7 +38,7 @@ fn run_metadata_collection_lint() { // Run collection as is std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - run_clippy_for_project("clippy_lints"); + run_clippy_for_package("clippy_lints"); // Check if cargo caching got in the way if let Ok(file) = File::open(metadata_output_path) { @@ -214,13 +61,13 @@ fn run_metadata_collection_lint() { .unwrap(); // Running the collection again - run_clippy_for_project("clippy_lints"); + run_clippy_for_package("clippy_lints"); } -fn run_clippy_for_project(project: &str) { +fn run_clippy_for_package(project: &str) { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let mut command = Command::new(&*CLIPPY_PATH); + let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); command .current_dir(root_dir.join(project)) @@ -233,8 +80,8 @@ fn run_clippy_for_project(project: &str) { .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { + // internal lints only exist if we build with the internal feature + if cfg!(feature = "internal") { command.args(&["-D", "clippy::internal"]); } diff --git a/src/tools/clippy/tests/fmt.rs b/src/tools/clippy/tests/fmt.rs index 383702dd439cf..0defd45b68b06 100644 --- a/src/tools/clippy/tests/fmt.rs +++ b/src/tools/clippy/tests/fmt.rs @@ -10,14 +10,6 @@ fn fmt() { return; } - // Skip this test if nightly rustfmt is unavailable - let rustup_output = Command::new("rustup").args(&["component", "list"]).output().unwrap(); - assert!(rustup_output.status.success()); - let component_output = String::from_utf8_lossy(&rustup_output.stdout); - if !component_output.contains("rustfmt") { - return; - } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let output = Command::new("cargo") .current_dir(root_dir) diff --git a/src/tools/clippy/tests/test_utils/mod.rs b/src/tools/clippy/tests/test_utils/mod.rs new file mode 100644 index 0000000000000..8a4de3f6def90 --- /dev/null +++ b/src/tools/clippy/tests/test_utils/mod.rs @@ -0,0 +1,13 @@ +#![allow(dead_code)] // see https://github.com/rust-lang/rust/issues/46379 + +use std::lazy::SyncLazy; +use std::path::PathBuf; + +pub static CARGO_CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| { + let mut path = std::env::current_exe().unwrap(); + assert!(path.pop()); // deps + path.set_file_name("cargo-clippy"); + path +}); + +pub const IS_RUSTC_TEST_SUITE: bool = option_env!("RUSTC_TEST_SUITE").is_some(); diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr index 0a8e5427978f5..0e85088691765 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr +++ b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr @@ -1,10 +1,16 @@ +error: invalid path + --> $DIR/invalid_paths.rs:15:5 + | +LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-paths` implied by `-D warnings` + error: invalid path --> $DIR/invalid_paths.rs:18:5 | LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-paths` implied by `-D warnings` error: invalid path --> $DIR/invalid_paths.rs:21:5 @@ -12,5 +18,5 @@ error: invalid path LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml index f1d4a4619c5dc..c902d21123dce 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -1,6 +1,8 @@ disallowed-methods = [ # just a string is shorthand for path only "std::iter::Iterator::sum", + "f32::clamp", + "slice::sort_unstable", # can give path and reason with an inline table { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index cb449b45bde85..338b3b5b28f42 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -7,6 +7,11 @@ fn main() { let re = Regex::new(r"ab.*c").unwrap(); re.is_match("abc"); - let a = vec![1, 2, 3, 4]; + let mut a = vec![1, 2, 3, 4]; a.iter().sum::<i32>(); + + a.sort_unstable(); + + let _ = 2.0f32.clamp(3.0f32, 4.0f32); + let _ = 2.0f64.clamp(3.0f64, 4.0f64); } diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 999ead10d5182..5533676aea287 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -20,5 +20,17 @@ error: use of a disallowed method `std::iter::Iterator::sum` LL | a.iter().sum::<i32>(); | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: use of a disallowed method `slice::sort_unstable` + --> $DIR/conf_disallowed_methods.rs:13:5 + | +LL | a.sort_unstable(); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed method `f32::clamp` + --> $DIR/conf_disallowed_methods.rs:15:13 + | +LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs index cd745feec6d8a..ba4394defbf20 100644 --- a/src/tools/clippy/tests/ui/as_conversions.rs +++ b/src/tools/clippy/tests/ui/as_conversions.rs @@ -1,6 +1,7 @@ // aux-build:macro_rules.rs #![warn(clippy::as_conversions)] +#![allow(clippy::borrow_as_ptr)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/as_conversions.stderr b/src/tools/clippy/tests/ui/as_conversions.stderr index f5f75d3aee04a..d11b56171b072 100644 --- a/src/tools/clippy/tests/ui/as_conversions.stderr +++ b/src/tools/clippy/tests/ui/as_conversions.stderr @@ -1,5 +1,5 @@ error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:14:13 + --> $DIR/as_conversions.rs:15:13 | LL | let i = 0u32 as u64; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = 0u32 as u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:16:13 + --> $DIR/as_conversions.rs:17:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:16:13 + --> $DIR/as_conversions.rs:17:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs index cb516d0f97783..7477c01ca7828 100644 --- a/src/tools/clippy/tests/ui/assertions_on_constants.rs +++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs @@ -1,4 +1,3 @@ -//FIXME: suggestions are wrongly expanded, this should be fixed along with #7843 #![allow(non_fmt_panics)] macro_rules! assert_const { diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.stderr b/src/tools/clippy/tests/ui/assertions_on_constants.stderr index ec80ec702fb57..e1f818814d500 100644 --- a/src/tools/clippy/tests/ui/assertions_on_constants.stderr +++ b/src/tools/clippy/tests/ui/assertions_on_constants.stderr @@ -1,75 +1,75 @@ error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:11:5 + --> $DIR/assertions_on_constants.rs:10:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(false)` should probably be replaced - --> $DIR/assertions_on_constants.rs:12:5 + --> $DIR/assertions_on_constants.rs:11:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:13:5 + --> $DIR/assertions_on_constants.rs:12:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced - --> $DIR/assertions_on_constants.rs:14:5 +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:13:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: use `panic!(..)` or `unreachable!(..)` + +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:16:5 + | +LL | assert!(false, "{}", msg.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!(..)` or `unreachable!(..)` error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:20:5 + --> $DIR/assertions_on_constants.rs:19:5 | LL | assert!(B); | ^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(false)` should probably be replaced - --> $DIR/assertions_on_constants.rs:23:5 + --> $DIR/assertions_on_constants.rs:22:5 | LL | assert!(C); | ^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced - --> $DIR/assertions_on_constants.rs:24:5 +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:23:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: use `panic!(..)` or `unreachable!(..)` error: `debug_assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:26:5 + --> $DIR/assertions_on_constants.rs:25:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `$crate::assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed new file mode 100644 index 0000000000000..ff5c6a8c3774b --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] + +fn main() { + let val = 1; + let _p = std::ptr::addr_of!(val); + + let mut val_mut = 1; + let _p_mut = std::ptr::addr_of_mut!(val_mut); +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs new file mode 100644 index 0000000000000..0f62ec6ee58b4 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] + +fn main() { + let val = 1; + let _p = &val as *const i32; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr new file mode 100644 index 0000000000000..be1ed73305674 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr @@ -0,0 +1,16 @@ +error: borrow as raw pointer + --> $DIR/borrow_as_ptr.rs:6:14 + | +LL | let _p = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + +error: borrow as raw pointer + --> $DIR/borrow_as_ptr.rs:9:18 + | +LL | let _p_mut = &mut val_mut as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed new file mode 100644 index 0000000000000..eaba3b1c20c8d --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed @@ -0,0 +1,22 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] +#![feature(lang_items, start, libc)] +#![no_std] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let val = 1; + let _p = core::ptr::addr_of!(val); + + let mut val_mut = 1; + let _p_mut = core::ptr::addr_of_mut!(val_mut); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs new file mode 100644 index 0000000000000..d83f9d1f875ba --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs @@ -0,0 +1,22 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] +#![feature(lang_items, start, libc)] +#![no_std] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let val = 1; + let _p = &val as *const i32; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr new file mode 100644 index 0000000000000..84c8ba7d07f1e --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr @@ -0,0 +1,16 @@ +error: borrow as raw pointer + --> $DIR/borrow_as_ptr_no_std.rs:9:14 + | +LL | let _p = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + +error: borrow as raw pointer + --> $DIR/borrow_as_ptr_no_std.rs:12:18 + | +LL | let _p_mut = &mut val_mut as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs index d011e84b115a7..659591fffbecd 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.rs +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -4,7 +4,13 @@ extern crate libc; #[warn(clippy::cast_ptr_alignment)] -#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)] +#[allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::cast_lossless, + clippy::borrow_as_ptr +)] + fn main() { /* These should be warned against */ diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr index 7998b787b91fb..aedd368445554 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.stderr +++ b/src/tools/clippy/tests/ui/cast_alignment.stderr @@ -1,5 +1,5 @@ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:12:5 + --> $DIR/cast_alignment.rs:18:5 | LL | (&1u8 as *const u8) as *const u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,19 +7,19 @@ LL | (&1u8 as *const u8) as *const u16; = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings` error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:13:5 + --> $DIR/cast_alignment.rs:19:5 | LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:16:5 + --> $DIR/cast_alignment.rs:22:5 | LL | (&1u8 as *const u8).cast::<u16>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:17:5 + --> $DIR/cast_alignment.rs:23:5 | LL | (&mut 1u8 as *mut u8).cast::<u16>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs index 089e5cfabe4b9..c48a734ba32c2 100644 --- a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs +++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs @@ -1,5 +1,5 @@ #![warn(clippy::cast_ref_to_mut)] -#![allow(clippy::no_effect)] +#![allow(clippy::no_effect, clippy::borrow_as_ptr)] extern "C" { // N.B., mutability can be easily incorrect in FFI calls -- as diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed index 0029fc673f110..2f489deb1ee1f 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.fixed +++ b/src/tools/clippy/tests/ui/deref_addrof.fixed @@ -37,6 +37,8 @@ fn main() { let b = &a; let b = *aref; + + let _ = unsafe { *core::ptr::addr_of!(a) }; } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs index f2f02dd5e723d..49f360b9a7f9e 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.rs +++ b/src/tools/clippy/tests/ui/deref_addrof.rs @@ -37,6 +37,8 @@ fn main() { let b = *&&a; let b = **&aref; + + let _ = unsafe { *core::ptr::addr_of!(a) }; } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr index 5bc1cbfa21510..75371fcdb9677 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.stderr +++ b/src/tools/clippy/tests/ui/deref_addrof.stderr @@ -49,7 +49,7 @@ LL | let b = **&aref; | ^^^^^^ help: try this: `aref` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:45:9 + --> $DIR/deref_addrof.rs:47:9 | LL | *& $visitor | ^^^^^^^^^^^ help: try this: `$visitor` @@ -60,7 +60,7 @@ LL | m!(self) = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:52:9 + --> $DIR/deref_addrof.rs:54:9 | LL | *& mut $visitor | ^^^^^^^^^^^^^^^ help: try this: `$visitor` diff --git a/src/tools/clippy/tests/ui/eq_op_macros.stderr b/src/tools/clippy/tests/ui/eq_op_macros.stderr index 885415b42c787..cd9f1826e59bf 100644 --- a/src/tools/clippy/tests/ui/eq_op_macros.stderr +++ b/src/tools/clippy/tests/ui/eq_op_macros.stderr @@ -21,6 +21,28 @@ LL | assert_in_macro_def!(); | = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:9:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ---------------------- in this macro invocation + | + = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:10:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ---------------------- in this macro invocation + | + = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) + error: identical args used in this `assert_eq!` macro call --> $DIR/eq_op_macros.rs:22:16 | @@ -45,28 +67,6 @@ error: identical args used in this `assert_ne!` macro call LL | assert_ne!(a + 1, a + 1); | ^^^^^^^^^^^^ -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_macros.rs:9:26 - | -LL | debug_assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ---------------------- in this macro invocation - | - = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_macros.rs:10:26 - | -LL | debug_assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ---------------------- in this macro invocation - | - = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) - error: identical args used in this `debug_assert_eq!` macro call --> $DIR/eq_op_macros.rs:38:22 | diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs index 1540062a4bc3e..ae2fad0086daa 100644 --- a/src/tools/clippy/tests/ui/erasing_op.rs +++ b/src/tools/clippy/tests/ui/erasing_op.rs @@ -1,3 +1,34 @@ +struct Length(u8); +struct Meter; + +impl core::ops::Mul<Meter> for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + +#[derive(Clone, Default, PartialEq, Eq, Hash)] +struct Vec1 { + x: i32, +} + +impl core::ops::Mul<Vec1> for i32 { + type Output = Vec1; + fn mul(self, mut right: Vec1) -> Vec1 { + right.x *= self; + right + } +} + +impl core::ops::Mul<i32> for Vec1 { + type Output = Vec1; + fn mul(mut self, right: i32) -> Vec1 { + self.x *= right; + self + } +} + #[allow(clippy::no_effect)] #[warn(clippy::erasing_op)] fn main() { @@ -6,4 +37,7 @@ fn main() { x * 0; 0 & x; 0 / x; + 0 * Meter; // no error: Output type is different from the non-zero argument + 0 * Vec1 { x: 5 }; + Vec1 { x: 5 } * 0; } diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr index e54ce85f98ec7..165ed9bfe58b1 100644 --- a/src/tools/clippy/tests/ui/erasing_op.stderr +++ b/src/tools/clippy/tests/ui/erasing_op.stderr @@ -1,5 +1,5 @@ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:6:5 + --> $DIR/erasing_op.rs:37:5 | LL | x * 0; | ^^^^^ @@ -7,16 +7,28 @@ LL | x * 0; = note: `-D clippy::erasing-op` implied by `-D warnings` error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:7:5 + --> $DIR/erasing_op.rs:38:5 | LL | 0 & x; | ^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:8:5 + --> $DIR/erasing_op.rs:39:5 | LL | 0 / x; | ^^^^^ -error: aborting due to 3 previous errors +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:41:5 + | +LL | 0 * Vec1 { x: 5 }; + | ^^^^^^^^^^^^^^^^^ + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:42:5 + | +LL | Vec1 { x: 5 } * 0; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index 1de79667f55fb..f938f71068844 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -248,3 +248,14 @@ mod type_param_bound { take(X::fun as fn()); } } + +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>` +fn arc_fp() { + let rc = std::rc::Rc::new(|| 7); + let arc = std::sync::Arc::new(|n| n + 1); + let ref_arc = &std::sync::Arc::new(|_| 5); + + true.then(|| rc()); + (0..5).map(|n| arc(n)); + Some(4).map(|n| ref_arc(n)); +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index 86abd347baa78..075bbc74922f9 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -248,3 +248,14 @@ mod type_param_bound { take(X::fun as fn()); } } + +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>` +fn arc_fp() { + let rc = std::rc::Rc::new(|| 7); + let arc = std::sync::Arc::new(|n| n + 1); + let ref_arc = &std::sync::Arc::new(|_| 5); + + true.then(|| rc()); + (0..5).map(|n| arc(n)); + Some(4).map(|n| ref_arc(n)); +} diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs index 2ed4b5db574d4..12bbda71f4348 100644 --- a/src/tools/clippy/tests/ui/identity_op.rs +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -11,6 +11,17 @@ impl std::ops::Shl<i32> for A { self } } + +struct Length(u8); +struct Meter; + +impl core::ops::Mul<Meter> for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + #[allow( clippy::eq_op, clippy::no_effect, @@ -53,4 +64,6 @@ fn main() { let mut a = A("".into()); let b = a << 0; // no error: non-integer + + 1 * Meter; // no error: non-integer } diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr index ff34b38db015b..0103cf5457e81 100644 --- a/src/tools/clippy/tests/ui/identity_op.stderr +++ b/src/tools/clippy/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:26:5 + --> $DIR/identity_op.rs:37:5 | LL | x + 0; | ^^^^^ @@ -7,73 +7,73 @@ LL | x + 0; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:27:5 + --> $DIR/identity_op.rs:38:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:29:5 + --> $DIR/identity_op.rs:40:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:32:5 + --> $DIR/identity_op.rs:43:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:35:5 + --> $DIR/identity_op.rs:46:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:36:5 + --> $DIR/identity_op.rs:47:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:42:5 + --> $DIR/identity_op.rs:53:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:45:5 + --> $DIR/identity_op.rs:56:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:48:5 + --> $DIR/identity_op.rs:59:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:49:5 + --> $DIR/identity_op.rs:60:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:50:5 + --> $DIR/identity_op.rs:61:5 | LL | 42 >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:51:5 + --> $DIR/identity_op.rs:62:5 | LL | &x >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:52:5 + --> $DIR/identity_op.rs:63:5 | LL | x >> &0; | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/issue_4266.stderr b/src/tools/clippy/tests/ui/issue_4266.stderr index 20419457b47f0..e5042aaa776b4 100644 --- a/src/tools/clippy/tests/ui/issue_4266.stderr +++ b/src/tools/clippy/tests/ui/issue_4266.stderr @@ -12,5 +12,14 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: methods called `new` usually take no `self` + --> $DIR/issue_4266.rs:27:22 + | +LL | pub async fn new(&mut self) -> Self { + | ^^^^^^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs index 377f760b3c4b2..2c91e02e84223 100644 --- a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs +++ b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs @@ -44,4 +44,24 @@ impl Iterator for Counter { } } +// Issue #8225 +trait Iter { + type I; + fn iter(&self) -> Self::I; +} + +impl Iter for () { + type I = core::slice::Iter<'static, ()>; + fn iter(&self) -> Self::I { + [].iter() + } +} + +struct S; +impl S { + fn iter(&self) -> <() as Iter>::I { + ().iter() + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr b/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr index 2273cd0be66ff..44f0295583695 100644 --- a/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr +++ b/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr @@ -12,5 +12,11 @@ error: this method is named `iter_mut` but its return type does not implement `I LL | fn iter_mut(&self) -> Counter2 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: this method is named `iter` but its return type does not implement `Iterator` + --> $DIR/iter_not_returning_iterator.rs:50:5 + | +LL | fn iter(&self) -> Self::I; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/manual_bits.fixed b/src/tools/clippy/tests/ui/manual_bits.fixed new file mode 100644 index 0000000000000..4f1b19b75b8a1 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_bits.fixed @@ -0,0 +1,48 @@ +// run-rustfix + +#![warn(clippy::manual_bits)] +#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] + +use std::mem::{size_of, size_of_val}; + +fn main() { + i8::BITS; + i16::BITS; + i32::BITS; + i64::BITS; + i128::BITS; + isize::BITS; + + u8::BITS; + u16::BITS; + u32::BITS; + u64::BITS; + u128::BITS; + usize::BITS; + + i8::BITS; + i16::BITS; + i32::BITS; + i64::BITS; + i128::BITS; + isize::BITS; + + u8::BITS; + u16::BITS; + u32::BITS; + u64::BITS; + u128::BITS; + usize::BITS; + + size_of::<usize>() * 4; + 4 * size_of::<usize>(); + size_of::<bool>() * 8; + 8 * size_of::<bool>(); + + size_of_val(&0u32) * 8; + + type Word = u32; + Word::BITS; + type Bool = bool; + size_of::<Bool>() * 8; +} diff --git a/src/tools/clippy/tests/ui/manual_bits.rs b/src/tools/clippy/tests/ui/manual_bits.rs new file mode 100644 index 0000000000000..f8a01313e6ad0 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_bits.rs @@ -0,0 +1,48 @@ +// run-rustfix + +#![warn(clippy::manual_bits)] +#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] + +use std::mem::{size_of, size_of_val}; + +fn main() { + size_of::<i8>() * 8; + size_of::<i16>() * 8; + size_of::<i32>() * 8; + size_of::<i64>() * 8; + size_of::<i128>() * 8; + size_of::<isize>() * 8; + + size_of::<u8>() * 8; + size_of::<u16>() * 8; + size_of::<u32>() * 8; + size_of::<u64>() * 8; + size_of::<u128>() * 8; + size_of::<usize>() * 8; + + 8 * size_of::<i8>(); + 8 * size_of::<i16>(); + 8 * size_of::<i32>(); + 8 * size_of::<i64>(); + 8 * size_of::<i128>(); + 8 * size_of::<isize>(); + + 8 * size_of::<u8>(); + 8 * size_of::<u16>(); + 8 * size_of::<u32>(); + 8 * size_of::<u64>(); + 8 * size_of::<u128>(); + 8 * size_of::<usize>(); + + size_of::<usize>() * 4; + 4 * size_of::<usize>(); + size_of::<bool>() * 8; + 8 * size_of::<bool>(); + + size_of_val(&0u32) * 8; + + type Word = u32; + size_of::<Word>() * 8; + type Bool = bool; + size_of::<Bool>() * 8; +} diff --git a/src/tools/clippy/tests/ui/manual_bits.stderr b/src/tools/clippy/tests/ui/manual_bits.stderr new file mode 100644 index 0000000000000..c4f5af2dcb0ec --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_bits.stderr @@ -0,0 +1,154 @@ +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:9:5 + | +LL | size_of::<i8>() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | + = note: `-D clippy::manual-bits` implied by `-D warnings` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:10:5 + | +LL | size_of::<i16>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:11:5 + | +LL | size_of::<i32>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:12:5 + | +LL | size_of::<i64>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:13:5 + | +LL | size_of::<i128>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:14:5 + | +LL | size_of::<isize>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:16:5 + | +LL | size_of::<u8>() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:17:5 + | +LL | size_of::<u16>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:18:5 + | +LL | size_of::<u32>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:19:5 + | +LL | size_of::<u64>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:20:5 + | +LL | size_of::<u128>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:21:5 + | +LL | size_of::<usize>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:23:5 + | +LL | 8 * size_of::<i8>(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:24:5 + | +LL | 8 * size_of::<i16>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:25:5 + | +LL | 8 * size_of::<i32>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:26:5 + | +LL | 8 * size_of::<i64>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:27:5 + | +LL | 8 * size_of::<i128>(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:28:5 + | +LL | 8 * size_of::<isize>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:30:5 + | +LL | 8 * size_of::<u8>(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:31:5 + | +LL | 8 * size_of::<u16>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:32:5 + | +LL | 8 * size_of::<u32>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:33:5 + | +LL | 8 * size_of::<u64>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:34:5 + | +LL | 8 * size_of::<u128>(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:35:5 + | +LL | 8 * size_of::<usize>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:45:5 + | +LL | size_of::<Word>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS` + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr index 2e3ebadd7b5d2..79d40c0bcb1d9 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -5,7 +5,7 @@ LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` @@ -16,7 +16,7 @@ LL | / for i in 3..src.len() { LL | | dst[count] = src[i]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].copy_from_slice(&src[3..]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:17:5 @@ -25,7 +25,7 @@ LL | / for i in 0..src.len() { LL | | dst[count] = src[i]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:23:5 @@ -34,7 +34,7 @@ LL | / for i in 0..src.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:29:5 @@ -43,7 +43,7 @@ LL | / for i in 3..(3 + src.len()) { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..(3 + src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].copy_from_slice(&src[..(3 + src.len() - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:35:5 @@ -52,7 +52,7 @@ LL | / for i in 5..src.len() { LL | | dst[i] = src[count - 2]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` + | |_____^ help: try replacing the loop by: `dst[5..src.len()].copy_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:41:5 @@ -61,7 +61,7 @@ LL | / for i in 0..dst.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[2..(dst.len() + 2)]);` + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[2..(dst.len() + 2)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:47:5 @@ -70,7 +70,7 @@ LL | / for i in 3..10 { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..10].copy_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:54:5 @@ -85,8 +85,8 @@ LL | | } | help: try replacing the loop by | -LL ~ dst[3..(src.len() + 3)].clone_from_slice(&src[..]); -LL + dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); +LL ~ dst[3..(src.len() + 3)].copy_from_slice(&src[..]); +LL + dst2[30..(src.len() + 30)].copy_from_slice(&src[..]); | error: it looks like you're manually copying between slices @@ -96,7 +96,7 @@ LL | / for i in 0..1 << 1 { LL | | dst[count] = src[i + 2]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].copy_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:71:5 @@ -105,7 +105,7 @@ LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; LL | | count += 1 LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);` error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs index 0083f94798fe4..ea0535d076b81 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs @@ -113,6 +113,17 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { for i in 0.. { dst[i] = src[i]; } + + // VecDeque - ideally this would work, but would require something like `range_as_slices` + let mut dst = std::collections::VecDeque::from_iter([0; 5]); + let src = std::collections::VecDeque::from_iter([0, 1, 2, 3, 4]); + for i in 0..dst.len() { + dst[i] = src[i]; + } + let src = vec![0, 1, 2, 3, 4]; + for i in 0..dst.len() { + dst[i] = src[i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr index 8ff0137a8120e..c163ae061dfc9 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -4,7 +4,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[..]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` @@ -14,7 +14,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i + 10] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:17:5 @@ -22,7 +22,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i] = src[i + 10]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:22:5 @@ -30,7 +30,7 @@ error: it looks like you're manually copying between slices LL | / for i in 11..src.len() { LL | | dst[i] = src[i - 10]; LL | | } - | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` + | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:27:5 @@ -38,7 +38,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..dst.len() { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:40:5 @@ -51,8 +51,8 @@ LL | | } | help: try replacing the loop by | -LL ~ dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); -LL + dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); +LL ~ dst[10..256].copy_from_slice(&src[(10 - 5)..(256 - 5)]); +LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices @@ -61,7 +61,7 @@ error: it looks like you're manually copying between slices LL | / for i in 10..LOOP_OFFSET { LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; LL | | } - | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:65:5 @@ -69,7 +69,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src_vec.len() { LL | | dst_vec[i] = src_vec[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:94:5 @@ -77,7 +77,7 @@ error: it looks like you're manually copying between slices LL | / for i in from..from + src.len() { LL | | dst[i] = src[i - from]; LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` + | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:98:5 @@ -85,7 +85,7 @@ error: it looks like you're manually copying between slices LL | / for i in from..from + 3 { LL | | dst[i] = src[i - from]; LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` + | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:103:5 @@ -93,7 +93,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..5 { LL | | dst[i - 0] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` + | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src[..5]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:108:5 @@ -101,10 +101,10 @@ error: it looks like you're manually copying between slices LL | / for i in 0..0 { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` + | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> $DIR/without_loop_counters.rs:120:5 + --> $DIR/without_loop_counters.rs:131:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i].clone(); diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr index 8bccbaefe23c5..91ebd695238bb 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr @@ -27,7 +27,6 @@ note: first possible panic found here | LL | panic!("This function panics") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:17:1 @@ -42,7 +41,6 @@ note: first possible panic found here | LL | todo!() | ^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:22:1 @@ -61,7 +59,6 @@ note: first possible panic found here | LL | panic!() | ^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:31:1 @@ -76,7 +73,6 @@ note: first possible panic found here | LL | if true { unreachable!() } else { panic!() } | ^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:36:1 @@ -92,7 +88,6 @@ note: first possible panic found here | LL | assert_eq!(x, 0); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:42:1 @@ -108,7 +103,6 @@ note: first possible panic found here | LL | assert_ne!(x, 0); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index b9d78b7f47924..47b3dad398977 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -1,5 +1,7 @@ #![warn(clippy::all)] #![warn(clippy::mutex_integer)] +#![warn(clippy::mutex_atomic)] +#![allow(clippy::borrow_as_ptr)] fn main() { use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index a3511ba708a88..262028a8723a3 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:6:5 + --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -7,31 +7,31 @@ LL | Mutex::new(true); = note: `-D clippy::mutex-atomic` implied by `-D warnings` error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:7:5 + --> $DIR/mutex_atomic.rs:9:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:8:5 + --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:10:5 + --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:11:5 + --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:12:5 + --> $DIR/mutex_atomic.rs:14:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | Mutex::new(0u32); = note: `-D clippy::mutex-integer` implied by `-D warnings` error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:13:5 + --> $DIR/mutex_atomic.rs:15:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index d6d6ab49734e7..87cdb3ace47cb 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 8eadc6ce3b47a..3f69cef301c8b 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr index 78d09b8b2108a..561503ae54fa2 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr @@ -14,7 +14,6 @@ note: return Err() instead of panicking | LL | panic!("error"); | ^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:11:5 @@ -31,7 +30,6 @@ note: return Err() instead of panicking | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:16:5 @@ -48,7 +46,6 @@ note: return Err() instead of panicking | LL | unreachable!(); | ^^^^^^^^^^^^^^ - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:21:5 @@ -65,7 +62,6 @@ note: return Err() instead of panicking | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:52:1 @@ -82,7 +78,6 @@ note: return Err() instead of panicking | LL | panic!("error"); | ^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:67:1 @@ -99,7 +94,6 @@ note: return Err() instead of panicking | LL | todo!("finish main method"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr index 7501d6d85edd7..b6aa005e7b521 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr @@ -15,7 +15,6 @@ note: return Err() instead of panicking | LL | assert!(x == 5, "wrong argument"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:13:5 @@ -33,7 +32,6 @@ note: return Err() instead of panicking | LL | assert_eq!(x, 5); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:19:5 @@ -51,7 +49,6 @@ note: return Err() instead of panicking | LL | assert_ne!(x, 1); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr index 2b607ff588895..bfd1c7a380149 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.stderr +++ b/src/tools/clippy/tests/ui/panicking_macros.stderr @@ -25,23 +25,18 @@ LL | todo!(); | ^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:17:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:18:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 @@ -50,23 +45,18 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:25:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:26:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:32:5 @@ -75,23 +65,18 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:33:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:34:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: `panic` should not be present in production code --> $DIR/panicking_macros.rs:40:5 @@ -104,24 +89,18 @@ error: `todo` should not be present in production code | LL | todo!(); | ^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:42:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:43:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/return_self_not_must_use.stderr b/src/tools/clippy/tests/ui/return_self_not_must_use.stderr index 3793a5559ba55..8af10cb65c406 100644 --- a/src/tools/clippy/tests/ui/return_self_not_must_use.stderr +++ b/src/tools/clippy/tests/ui/return_self_not_must_use.stderr @@ -5,6 +5,7 @@ LL | fn what(&self) -> Self; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::return-self-not-must-use` implied by `-D warnings` + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` --> $DIR/return_self_not_must_use.rs:17:5 @@ -13,6 +14,8 @@ LL | / pub fn foo(&self) -> Self { LL | | Self LL | | } | |_____^ + | + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` --> $DIR/return_self_not_must_use.rs:20:5 @@ -21,6 +24,8 @@ LL | / pub fn bar(self) -> Self { LL | | self LL | | } | |_____^ + | + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.rs b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs new file mode 100644 index 0000000000000..261d8bc7260ca --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs @@ -0,0 +1,43 @@ +#![warn(clippy::single_char_lifetime_names)] + +// Lifetimes should only be linted when they're introduced +struct DiagnosticCtx<'a, 'b> +where + 'a: 'b, +{ + _source: &'a str, + _unit: &'b (), +} + +// Only the lifetimes on the `impl`'s generics should be linted +impl<'a, 'b> DiagnosticCtx<'a, 'b> { + fn new(source: &'a str, unit: &'b ()) -> DiagnosticCtx<'a, 'b> { + Self { + _source: source, + _unit: unit, + } + } +} + +// No lifetimes should be linted here +impl<'src, 'unit> DiagnosticCtx<'src, 'unit> { + fn new_pass(source: &'src str, unit: &'unit ()) -> DiagnosticCtx<'src, 'unit> { + Self { + _source: source, + _unit: unit, + } + } +} + +// Only 'a should be linted here +fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { + base.split_once(other) + .map(|(left, right)| (left, Some(right))) + .unwrap_or((base, None)) +} + +fn main() { + let src = "loop {}"; + let unit = (); + DiagnosticCtx::new(src, &unit); +} diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr new file mode 100644 index 0000000000000..013b64f46a8ce --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr @@ -0,0 +1,43 @@ +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:4:22 + | +LL | struct DiagnosticCtx<'a, 'b> + | ^^ + | + = note: `-D clippy::single-char-lifetime-names` implied by `-D warnings` + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:4:26 + | +LL | struct DiagnosticCtx<'a, 'b> + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:13:6 + | +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:13:10 + | +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:33:15 + | +LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { + | ^^ + | + = help: use a more informative name + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed index ef518359ec5f3..3329efbd4ff42 100644 --- a/src/tools/clippy/tests/ui/swap.fixed +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -122,3 +122,36 @@ fn main() { ; std::mem::swap(&mut c.0, &mut a); } + +fn issue_8154() { + struct S1 { + x: i32, + y: i32, + } + struct S2(S1); + struct S3<'a, 'b>(&'a mut &'b mut S1); + + impl std::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl. + let mut s = S2(S1 { x: 0, y: 0 }); + let t = s.x; + s.x = s.y; + s.y = t; + + // Accessing through a mutable reference is fine + let mut s = S1 { x: 0, y: 0 }; + let mut s = &mut s; + let s = S3(&mut s); + std::mem::swap(&mut s.0.x, &mut s.0.y); +} diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs index 8518659ccf316..8179ac1f2ab02 100644 --- a/src/tools/clippy/tests/ui/swap.rs +++ b/src/tools/clippy/tests/ui/swap.rs @@ -144,3 +144,38 @@ fn main() { c.0 = a; a = t; } + +fn issue_8154() { + struct S1 { + x: i32, + y: i32, + } + struct S2(S1); + struct S3<'a, 'b>(&'a mut &'b mut S1); + + impl std::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl. + let mut s = S2(S1 { x: 0, y: 0 }); + let t = s.x; + s.x = s.y; + s.y = t; + + // Accessing through a mutable reference is fine + let mut s = S1 { x: 0, y: 0 }; + let mut s = &mut s; + let s = S3(&mut s); + let t = s.0.x; + s.0.x = s.0.y; + s.0.y = t; +} diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr index 614d16ced40f1..2b556b475cee9 100644 --- a/src/tools/clippy/tests/ui/swap.stderr +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -108,5 +108,15 @@ LL | | a = c.0; | = note: or maybe you should use `std::mem::replace`? -error: aborting due to 12 previous errors +error: this looks like you are swapping `s.0.x` and `s.0.y` manually + --> $DIR/swap.rs:178:5 + | +LL | / let t = s.0.x; +LL | | s.0.x = s.0.y; +LL | | s.0.y = t; + | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)` + | + = note: or maybe you should use `std::mem::replace`? + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs index cb2b0054e352b..2edb202892afc 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -28,4 +28,49 @@ where unimplemented!(); } +trait T: Default { + fn f() + where + Self: Default; +} + +trait U: Default { + fn f() + where + Self: Clone; +} + +trait ZZ: Default { + fn g(); + fn h(); + fn f() + where + Self: Default + Clone; +} + +trait BadTrait: Default + Clone { + fn f() + where + Self: Default + Clone; + fn g() + where + Self: Default; + fn h() + where + Self: Copy; +} + +#[derive(Default, Clone)] +struct Life {} + +impl T for Life { + // this should not warn + fn f() {} +} + +impl U for Life { + // this should not warn + fn f() {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr index 027e1c7520412..e0c7a7ec618ed 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr @@ -19,5 +19,45 @@ LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) | = help: consider removing this trait bound -error: aborting due to 2 previous errors +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:34:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:48:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:54:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:54:25 + | +LL | Self: Default + Clone; + | ^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:57:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 6a7037d8f3826..9b681a79aae7a 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::borrow_as_ptr)] extern crate core; diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs index 9e213aab68c57..f06ffab5d9be9 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -1,4 +1,5 @@ #![warn(clippy::transmute_ptr_to_ptr)] +#![allow(clippy::borrow_as_ptr)] // Make sure we can modify lifetimes, which is one of the recommended uses // of transmute diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr index 4d1b8fcc199e8..49a8a3347e404 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer to a pointer - --> $DIR/transmute_ptr_to_ptr.rs:29:29 + --> $DIR/transmute_ptr_to_ptr.rs:30:29 | LL | let _: *const f32 = std::mem::transmute(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` @@ -7,31 +7,31 @@ LL | let _: *const f32 = std::mem::transmute(ptr); = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmute_ptr_to_ptr.rs:30:27 + --> $DIR/transmute_ptr_to_ptr.rs:31:27 | LL | let _: *mut f32 = std::mem::transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:32:23 + --> $DIR/transmute_ptr_to_ptr.rs:33:23 | LL | let _: &f32 = std::mem::transmute(&1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:33:23 + --> $DIR/transmute_ptr_to_ptr.rs:34:23 | LL | let _: &f64 = std::mem::transmute(&1f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:36:27 + --> $DIR/transmute_ptr_to_ptr.rs:37:27 | LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:37:37 + --> $DIR/transmute_ptr_to_ptr.rs:38:37 | LL | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)` diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed index b6f1e83181ccb..9ae0ed0b13f1e 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -4,8 +4,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused_unsafe)] -#![allow(dead_code)] +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)] use std::mem::{size_of, transmute}; diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs index 0205d1ece60d5..985cf9a075d31 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -4,8 +4,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused_unsafe)] -#![allow(dead_code)] +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)] use std::mem::{size_of, transmute}; diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 1157b179317e2..e8496a325d6dc 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,5 +1,5 @@ error: transmute from an integer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:18:39 | LL | let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` @@ -7,7 +7,7 @@ LL | let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize: = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:22:38 | LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` @@ -15,13 +15,13 @@ LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46 | LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50 | LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` @@ -29,25 +29,25 @@ LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, us = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:40:41 | LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41 | LL | let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49 | LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index 766190f209977..fc740ee11d6ab 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -69,4 +69,14 @@ mod issue4326 { } } +// Issue #7360 +struct Foo<T, U> +where + T: Clone, + U: Clone, +{ + t: T, + u: U, +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr index 2b5a7b348b982..824506a4257ef 100644 --- a/src/tools/clippy/tests/ui/unit_cmp.stderr +++ b/src/tools/clippy/tests/ui/unit_cmp.stderr @@ -33,8 +33,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: `debug_assert_eq` of unit values detected. This will always succeed --> $DIR/unit_cmp.rs:32:5 @@ -47,8 +45,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `$crate::assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert_ne` of unit values detected. This will always fail --> $DIR/unit_cmp.rs:41:5 @@ -61,8 +57,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: `debug_assert_ne` of unit values detected. This will always fail --> $DIR/unit_cmp.rs:49:5 @@ -75,8 +69,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `$crate::assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed index bda0f2c47cdd5..3332f49c80c97 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::nonstandard_macro_braces, + clippy::borrow_as_ptr +)] fn main() { // casting integer literal to float is unnecessary diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs index f7a4f2a5988fd..ec01e93877927 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::nonstandard_macro_braces, + clippy::borrow_as_ptr +)] fn main() { // casting integer literal to float is unnecessary diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr index 3695a8f819c4a..a281143281b54 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr @@ -1,5 +1,5 @@ error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:8:5 + --> $DIR/unnecessary_cast_fixable.rs:13:5 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` @@ -7,97 +7,97 @@ LL | 100 as f32; = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:9:5 + --> $DIR/unnecessary_cast_fixable.rs:14:5 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:10:5 + --> $DIR/unnecessary_cast_fixable.rs:15:5 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:11:13 + --> $DIR/unnecessary_cast_fixable.rs:16:13 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:12:13 + --> $DIR/unnecessary_cast_fixable.rs:17:13 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:13:13 + --> $DIR/unnecessary_cast_fixable.rs:18:13 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:14:5 + --> $DIR/unnecessary_cast_fixable.rs:19:5 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:15:5 + --> $DIR/unnecessary_cast_fixable.rs:20:5 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:32:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:33:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:34:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:30:5 + --> $DIR/unnecessary_cast_fixable.rs:35:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:31:5 + --> $DIR/unnecessary_cast_fixable.rs:36:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 + --> $DIR/unnecessary_cast_fixable.rs:38:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 + --> $DIR/unnecessary_cast_fixable.rs:39:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:38:13 + --> $DIR/unnecessary_cast_fixable.rs:43:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:39:13 + --> $DIR/unnecessary_cast_fixable.rs:44:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs index 8b141e25942d7..4b059558173fb 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.rs +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![warn(clippy::unused_io_amount)] +extern crate futures; +use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use std::io::{self, Read}; fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> { @@ -61,4 +63,55 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +async fn bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) { + w.write(b"hello world").await.unwrap(); +} + +async fn bad_async_read<R: AsyncRead + Unpin>(r: &mut R) { + let mut buf = [0u8; 0]; + r.read(&mut buf[..]).await.unwrap(); +} + +async fn io_not_ignored_async_write<W: AsyncWrite + Unpin>(mut w: W) { + // Here we're forgetting to await the future, so we should get a + // warning about _that_ (or we would, if it were enabled), but we + // won't get one about ignoring the return value. + w.write(b"hello world"); +} + +fn bad_async_write_closure<W: AsyncWrite + Unpin + 'static>(w: W) -> impl futures::Future<Output = io::Result<()>> { + let mut w = w; + async move { + w.write(b"hello world").await?; + Ok(()) + } +} + +async fn async_read_nested_or<R: AsyncRead + Unpin>(r: &mut R, do_it: bool) -> Result<[u8; 1], Error> { + let mut buf = [0u8; 1]; + if do_it { + r.read(&mut buf[..]).await.or(Err(Error::Kind))?; + } + Ok(buf) +} + +use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _}; + +async fn bad_async_write_tokio<W: TokioAsyncWrite + Unpin>(w: &mut W) { + w.write(b"hello world").await.unwrap(); +} + +async fn bad_async_read_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) { + let mut buf = [0u8; 0]; + r.read(&mut buf[..]).await.unwrap(); +} + +async fn undetected_bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) { + // It would be good to detect this case some day, but the current lint + // doesn't handle it. (The documentation says that this lint "detects + // only common patterns".) + let future = w.write(b"Hello world"); + future.await.unwrap(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr index d8dfc0e5a798c..e5bdd993aa1ad 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.stderr +++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr @@ -1,61 +1,74 @@ -error: written amount is not handled. Use `Write::write_all` instead - --> $DIR/unused_io_amount.rs:7:5 +error: written amount is not handled + --> $DIR/unused_io_amount.rs:9:5 | LL | s.write(b"test")?; | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unused-io-amount` implied by `-D warnings` + = help: use `Write::write_all` instead, or handle partial writes -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:9:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:11:5 | LL | s.read(&mut buf)?; | ^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: written amount is not handled. Use `Write::write_all` instead - --> $DIR/unused_io_amount.rs:14:5 +error: written amount is not handled + --> $DIR/unused_io_amount.rs:16:5 | LL | s.write(b"test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:16:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:18:5 | LL | s.read(&mut buf).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:20:5 + --> $DIR/unused_io_amount.rs:22:5 | LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: written amount is not handled - --> $DIR/unused_io_amount.rs:21:5 + --> $DIR/unused_io_amount.rs:23:5 | LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:28:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:30:5 | LL | reader.read(&mut result).ok()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:37:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:39:5 | LL | reader.read(&mut result).or_else(|err| Err(err))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:49:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:51:5 | LL | reader.read(&mut result).or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:56:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:58:5 | LL | / reader LL | | .read(&mut result) @@ -63,6 +76,56 @@ LL | | .or(Err(Error::Kind)) LL | | .or(Err(Error::Kind)) LL | | .expect("error"); | |________________________^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:67:5 + | +LL | w.write(b"hello world").await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:72:5 + | +LL | r.read(&mut buf[..]).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:85:9 + | +LL | w.write(b"hello world").await?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:93:9 + | +LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:101:5 + | +LL | w.write(b"hello world").await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:106:5 + | +LL | r.read(&mut buf[..]).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 10 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs index c91d96ee18a31..a9a4a0f5a6b5c 100644 --- a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs @@ -4,6 +4,8 @@ use std::rc::Rc; use std::sync::Arc; #[warn(clippy::vtable_address_comparisons)] +#[allow(clippy::borrow_as_ptr)] + fn main() { let a: *const dyn Debug = &1 as &dyn Debug; let b: *const dyn Debug = &1 as &dyn Debug; diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr index 76bd57217d784..2f1be61e5df72 100644 --- a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr @@ -1,5 +1,5 @@ error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:12:13 + --> $DIR/vtable_address_comparisons.rs:14:13 | LL | let _ = a == b; | ^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = a == b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:13:13 + --> $DIR/vtable_address_comparisons.rs:15:13 | LL | let _ = a != b; | ^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = a != b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:14:13 + --> $DIR/vtable_address_comparisons.rs:16:13 | LL | let _ = a < b; | ^^^^^ @@ -24,7 +24,7 @@ LL | let _ = a < b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:15:13 + --> $DIR/vtable_address_comparisons.rs:17:13 | LL | let _ = a <= b; | ^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = a <= b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:16:13 + --> $DIR/vtable_address_comparisons.rs:18:13 | LL | let _ = a > b; | ^^^^^ @@ -40,7 +40,7 @@ LL | let _ = a > b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:17:13 + --> $DIR/vtable_address_comparisons.rs:19:13 | LL | let _ = a >= b; | ^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = a >= b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:18:5 + --> $DIR/vtable_address_comparisons.rs:20:5 | LL | ptr::eq(a, b); | ^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | ptr::eq(a, b); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:22:5 + --> $DIR/vtable_address_comparisons.rs:24:5 | LL | ptr::eq(a, b); | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | ptr::eq(a, b); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:25:5 + --> $DIR/vtable_address_comparisons.rs:27:5 | LL | Rc::ptr_eq(&a, &a); | ^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | Rc::ptr_eq(&a, &a); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:28:5 + --> $DIR/vtable_address_comparisons.rs:30:5 | LL | Arc::ptr_eq(&a, &a); | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed index 1e74ad2de655a..cb8892a3f009b 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed @@ -372,6 +372,36 @@ fn exact_match_with_single_field() { } } +fn custom_deref() { + struct S1<T> { + x: T, + } + struct S2<T>(S1<T>); + impl<T> core::ops::Deref for S2<T> { + type Target = S1<T>; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<T> core::ops::DerefMut for S2<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + let mut s = S2(S1 { x: 0..10 }); + for x in s.x.by_ref() { + println!("{}", x); + } +} + +fn issue_8113() { + let mut x = [0..10]; + for x in x[0].by_ref() { + println!("{}", x); + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs index 69cb636cee826..d91571844959e 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs @@ -372,6 +372,36 @@ fn exact_match_with_single_field() { } } +fn custom_deref() { + struct S1<T> { + x: T, + } + struct S2<T>(S1<T>); + impl<T> core::ops::Deref for S2<T> { + type Target = S1<T>; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<T> core::ops::DerefMut for S2<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + let mut s = S2(S1 { x: 0..10 }); + while let Some(x) = s.x.next() { + println!("{}", x); + } +} + +fn issue_8113() { + let mut x = [0..10]; + while let Some(x) = x[0].next() { + println!("{}", x); + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr index 1a11ba26eef0f..fb2b0f2467c0d 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr @@ -123,10 +123,22 @@ LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:377:5 + --> $DIR/while_let_on_iterator.rs:393:5 + | +LL | while let Some(x) = s.x.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:400:5 + | +LL | while let Some(x) = x[0].next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:407:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 21 previous errors +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs index 1b9da8a55e53f..f8fee4b3ab2d8 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -188,3 +188,24 @@ mod issue6727 { } } } + +pub mod issue8142 { + struct S; + + impl S { + // Should lint: is_ methods should only take &self, or no self at all. + fn is_still_buggy(&mut self) -> bool { + false + } + + // Should not lint: "no self at all" is allowed. + fn is_forty_two(x: u32) -> bool { + x == 42 + } + + // Should not lint: &self is allowed. + fn is_test_code(&self) -> bool { + true + } + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index 590ee6d9c529d..5493a99572e06 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -191,5 +191,13 @@ LL | fn to_u64(self) -> u64 { | = help: consider choosing a less ambiguous name -error: aborting due to 24 previous errors +error: methods called `is_*` usually take `self` by reference or no `self` + --> $DIR/wrong_self_convention.rs:197:27 + | +LL | fn is_still_buggy(&mut self) -> bool { + | ^^^^^^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/zero_offset.rs b/src/tools/clippy/tests/ui/zero_offset.rs index 6c190a4c86c48..fd9ac1fa7663b 100644 --- a/src/tools/clippy/tests/ui/zero_offset.rs +++ b/src/tools/clippy/tests/ui/zero_offset.rs @@ -1,3 +1,4 @@ +#[allow(clippy::borrow_as_ptr)] fn main() { unsafe { let m = &mut () as *mut (); diff --git a/src/tools/clippy/tests/ui/zero_offset.stderr b/src/tools/clippy/tests/ui/zero_offset.stderr index b12c8e9a73c6d..481a446571ab0 100644 --- a/src/tools/clippy/tests/ui/zero_offset.stderr +++ b/src/tools/clippy/tests/ui/zero_offset.stderr @@ -1,5 +1,5 @@ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:4:9 + --> $DIR/zero_offset.rs:5:9 | LL | m.offset(0); | ^^^^^^^^^^^ @@ -7,43 +7,43 @@ LL | m.offset(0); = note: `#[deny(clippy::zst_offset)]` on by default error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:5:9 + --> $DIR/zero_offset.rs:6:9 | LL | m.wrapping_add(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:6:9 + --> $DIR/zero_offset.rs:7:9 | LL | m.sub(0); | ^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:7:9 + --> $DIR/zero_offset.rs:8:9 | LL | m.wrapping_sub(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:10:9 + --> $DIR/zero_offset.rs:11:9 | LL | c.offset(0); | ^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:11:9 + --> $DIR/zero_offset.rs:12:9 | LL | c.wrapping_add(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:12:9 + --> $DIR/zero_offset.rs:13:9 | LL | c.sub(0); | ^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:13:9 + --> $DIR/zero_offset.rs:14:9 | LL | c.wrapping_sub(0); | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/workspace.rs b/src/tools/clippy/tests/workspace.rs new file mode 100644 index 0000000000000..677b4a4d56999 --- /dev/null +++ b/src/tools/clippy/tests/workspace.rs @@ -0,0 +1,107 @@ +#![feature(once_cell)] + +use std::path::PathBuf; +use std::process::Command; +use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE}; + +mod test_utils; + +#[test] +fn test_no_deps_ignores_path_deps_in_workspaces() { + if IS_RUSTC_TEST_SUITE { + return; + } + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = root.join("target").join("workspace_test"); + let cwd = root.join("tests/workspace_test"); + + // Make sure we start with a clean state + Command::new("cargo") + .current_dir(&cwd) + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); + + // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. + // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--no-deps") + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + let lint_path_dep = || { + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + assert!( + String::from_utf8(output.stderr) + .unwrap() + .contains("error: empty `loop {}` wastes CPU cycles") + ); + }; + + // Make sure Cargo is aware of the removal of `--no-deps`. + lint_path_dep(); + + let successful_build = || { + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + output + }; + + // Trigger a sucessful build, so Cargo would like to cache the build result. + successful_build(); + + // Make sure there's no spurious rebuild when nothing changes. + let stderr = String::from_utf8(successful_build().stderr).unwrap(); + assert!(!stderr.contains("Compiling")); + assert!(!stderr.contains("Checking")); + assert!(stderr.contains("Finished")); + + // Make sure Cargo is aware of the new `--cfg` flag. + lint_path_dep(); +} diff --git a/src/tools/clippy/clippy_workspace_tests/Cargo.toml b/src/tools/clippy/tests/workspace_test/Cargo.toml similarity index 71% rename from src/tools/clippy/clippy_workspace_tests/Cargo.toml rename to src/tools/clippy/tests/workspace_test/Cargo.toml index 7a235b215d38d..bf5b4ca5288a4 100644 --- a/src/tools/clippy/clippy_workspace_tests/Cargo.toml +++ b/src/tools/clippy/tests/workspace_test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "clippy_workspace_tests" +name = "workspace_test" version = "0.1.0" edition = "2018" diff --git a/src/tools/clippy/clippy_workspace_tests/build.rs b/src/tools/clippy/tests/workspace_test/build.rs similarity index 100% rename from src/tools/clippy/clippy_workspace_tests/build.rs rename to src/tools/clippy/tests/workspace_test/build.rs diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml b/src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml similarity index 100% rename from src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml rename to src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs b/src/tools/clippy/tests/workspace_test/path_dep/src/lib.rs similarity index 100% rename from src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs rename to src/tools/clippy/tests/workspace_test/path_dep/src/lib.rs diff --git a/src/tools/clippy/clippy_workspace_tests/src/main.rs b/src/tools/clippy/tests/workspace_test/src/main.rs similarity index 100% rename from src/tools/clippy/clippy_workspace_tests/src/main.rs rename to src/tools/clippy/tests/workspace_test/src/main.rs diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml b/src/tools/clippy/tests/workspace_test/subcrate/Cargo.toml similarity index 100% rename from src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml rename to src/tools/clippy/tests/workspace_test/subcrate/Cargo.toml diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs b/src/tools/clippy/tests/workspace_test/subcrate/src/lib.rs similarity index 100% rename from src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs rename to src/tools/clippy/tests/workspace_test/subcrate/src/lib.rs