From 88fda58bf5396667a022461f9d7465f24fd3395d Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 1 Aug 2024 14:38:18 +0200 Subject: [PATCH 01/12] core: use `compare_bytes` for more slice element types --- library/core/src/slice/cmp.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index d19d0eae16671..dd43b58ec0f58 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -3,7 +3,8 @@ use super::{from_raw_parts, memchr}; use crate::cmp::{self, BytewiseEq, Ordering}; use crate::intrinsics::compare_bytes; -use crate::mem; +use crate::num::NonZero; +use crate::{ascii, mem}; #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq<[U]> for [T] @@ -182,19 +183,31 @@ impl SliceOrd for A { } } +// The type should be treated as an unsigned byte for comparisons. +#[rustc_specialization_trait] +unsafe trait UnsignedByte {} + +unsafe impl UnsignedByte for bool {} +unsafe impl UnsignedByte for u8 {} +unsafe impl UnsignedByte for NonZero {} +unsafe impl UnsignedByte for Option> {} +unsafe impl UnsignedByte for ascii::Char {} + // `compare_bytes` compares a sequence of unsigned bytes lexicographically. -// this matches the order we want for [u8], but no others (not even [i8]). -impl SliceOrd for u8 { +impl SliceOrd for A { #[inline] fn compare(left: &[Self], right: &[Self]) -> Ordering { // Since the length of a slice is always less than or equal to isize::MAX, this never underflows. let diff = left.len() as isize - right.len() as isize; // This comparison gets optimized away (on x86_64 and ARM) because the subtraction updates flags. let len = if left.len() < right.len() { left.len() } else { right.len() }; + let left = left.as_ptr().cast(); + let right = right.as_ptr().cast(); // SAFETY: `left` and `right` are references and are thus guaranteed to be valid. - // We use the minimum of both lengths which guarantees that both regions are - // valid for reads in that interval. - let mut order = unsafe { compare_bytes(left.as_ptr(), right.as_ptr(), len) as isize }; + // `UnsignedByte` is only implemented for types that are valid u8s. We use the + // minimum of both lengths which guarantees that both regions are valid for reads + // in that interval. + let mut order = unsafe { compare_bytes(left, right, len) as isize }; if order == 0 { order = diff; } From 32d6d0e187c84ec55b8a4aeb4a1bcd267aa5096f Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 1 Aug 2024 15:44:49 +0200 Subject: [PATCH 02/12] bless miri tests --- .../miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr | 4 ++-- .../uninit/uninit_alloc_diagnostic_with_provenance.stderr | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr index 960cae9012484..dfe3ed4b522ab 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: reading memory at ALLOC[0x0..0x10], but memory is uninitialized at [0x4..0x10], and this operation requires initialized memory --> RUSTLIB/core/src/slice/cmp.rs:LL:CC | -LL | let mut order = unsafe { compare_bytes(left.as_ptr(), right.as_ptr(), len) as isize }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading memory at ALLOC[0x0..0x10], but memory is uninitialized at [0x4..0x10], and this operation requires initialized memory +LL | let mut order = unsafe { compare_bytes(left, right, len) as isize }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading memory at ALLOC[0x0..0x10], but memory is uninitialized at [0x4..0x10], and this operation requires initialized memory | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr index 5439418f26771..48ce9ae76568a 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: reading memory at ALLOC[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory --> RUSTLIB/core/src/slice/cmp.rs:LL:CC | -LL | let mut order = unsafe { compare_bytes(left.as_ptr(), right.as_ptr(), len) as isize }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading memory at ALLOC[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory +LL | let mut order = unsafe { compare_bytes(left, right, len) as isize }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading memory at ALLOC[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information From 341511ad4e0405f606d434265b00120f782914d0 Mon Sep 17 00:00:00 2001 From: Konippi Date: Sun, 4 Aug 2024 21:39:42 +0900 Subject: [PATCH 03/12] refactor: standardize duplicate processes in parser --- library/core/src/net/parser.rs | 56 +++++++++++++++------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/library/core/src/net/parser.rs b/library/core/src/net/parser.rs index a8ec71f0dd801..73230f6ee5b03 100644 --- a/library/core/src/net/parser.rs +++ b/library/core/src/net/parser.rs @@ -112,18 +112,18 @@ impl<'a> Parser<'a> { max_digits: Option, allow_zero_prefix: bool, ) -> Option { - // If max_digits.is_some(), then we are parsing a `u8` or `u16` and - // don't need to use checked arithmetic since it fits within a `u32`. - if let Some(max_digits) = max_digits { - // u32::MAX = 4_294_967_295u32, which is 10 digits long. - // `max_digits` must be less than 10 to not overflow a `u32`. - debug_assert!(max_digits < 10); - - self.read_atomically(move |p| { - let mut result = 0_u32; - let mut digit_count = 0; - let has_leading_zero = p.peek_char() == Some('0'); + self.read_atomically(move |p| { + let mut digit_count = 0; + let has_leading_zero = p.peek_char() == Some('0'); + + // If max_digits.is_some(), then we are parsing a `u8` or `u16` and + // don't need to use checked arithmetic since it fits within a `u32`. + let result = if let Some(max_digits) = max_digits { + // u32::MAX = 4_294_967_295u32, which is 10 digits long. + // `max_digits` must be less than 10 to not overflow a `u32`. + debug_assert!(max_digits < 10); + let mut result = 0_u32; while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { result *= radix; result += digit; @@ -134,19 +134,9 @@ impl<'a> Parser<'a> { } } - if digit_count == 0 { - None - } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { - None - } else { - result.try_into().ok() - } - }) - } else { - self.read_atomically(move |p| { + result.try_into().ok() + } else { let mut result = T::ZERO; - let mut digit_count = 0; - let has_leading_zero = p.peek_char() == Some('0'); while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { result = result.checked_mul(radix)?; @@ -154,15 +144,17 @@ impl<'a> Parser<'a> { digit_count += 1; } - if digit_count == 0 { - None - } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { - None - } else { - Some(result) - } - }) - } + Some(result) + }; + + if digit_count == 0 { + None + } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { + None + } else { + result + } + }) } /// Reads an IPv4 address. From 8301dd4767ce0d9d6dd1a06ff5f91319a42263e5 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 12 Aug 2024 10:20:32 +0200 Subject: [PATCH 04/12] core: make documentation clearer, rename slice comparison specialization trait --- library/core/src/slice/cmp.rs | 40 ++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index dd43b58ec0f58..1769612def0a5 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -183,30 +183,40 @@ impl SliceOrd for A { } } -// The type should be treated as an unsigned byte for comparisons. +/// Marks that a type should be treated as an unsigned byte for comparisons. +/// +/// # Safety +/// * The type must be readable as an `u8`, meaning it has to have the same +/// layout as `u8` and always be initialized. +/// * For every `x` and `y` of this type, `Ord(x, y)` must return the same +/// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. #[rustc_specialization_trait] -unsafe trait UnsignedByte {} +unsafe trait UnsignedBytewiseOrd {} -unsafe impl UnsignedByte for bool {} -unsafe impl UnsignedByte for u8 {} -unsafe impl UnsignedByte for NonZero {} -unsafe impl UnsignedByte for Option> {} -unsafe impl UnsignedByte for ascii::Char {} +unsafe impl UnsignedBytewiseOrd for bool {} +unsafe impl UnsignedBytewiseOrd for u8 {} +unsafe impl UnsignedBytewiseOrd for NonZero {} +unsafe impl UnsignedBytewiseOrd for Option> {} +unsafe impl UnsignedBytewiseOrd for ascii::Char {} -// `compare_bytes` compares a sequence of unsigned bytes lexicographically. -impl SliceOrd for A { +// `compare_bytes` compares a sequence of unsigned bytes lexicographically, so +// use it if the requirements for `UnsignedBytewiseOrd` are fulfilled. +impl SliceOrd for A { #[inline] fn compare(left: &[Self], right: &[Self]) -> Ordering { - // Since the length of a slice is always less than or equal to isize::MAX, this never underflows. + // Since the length of a slice is always less than or equal to + // isize::MAX, this never underflows. let diff = left.len() as isize - right.len() as isize; - // This comparison gets optimized away (on x86_64 and ARM) because the subtraction updates flags. + // This comparison gets optimized away (on x86_64 and ARM) because the + // subtraction updates flags. let len = if left.len() < right.len() { left.len() } else { right.len() }; let left = left.as_ptr().cast(); let right = right.as_ptr().cast(); - // SAFETY: `left` and `right` are references and are thus guaranteed to be valid. - // `UnsignedByte` is only implemented for types that are valid u8s. We use the - // minimum of both lengths which guarantees that both regions are valid for reads - // in that interval. + // SAFETY: `left` and `right` are references and are thus guaranteed to + // be valid. `UnsignedBytewiseOrd` is only implemented for types that + // are valid u8s and can be compared the same way. We use the minimum + // of both lengths which guarantees that both regions are valid for + // reads in that interval. let mut order = unsafe { compare_bytes(left, right, len) as isize }; if order == 0 { order = diff; From 1a2ec2634e378c8e1b8c531f29c4f983b87a5764 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 27 Aug 2024 19:10:22 -0400 Subject: [PATCH 05/12] Make option-like-enum.rs UB-free and portable --- tests/debuginfo/option-like-enum.rs | 39 +++++++++++------------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/tests/debuginfo/option-like-enum.rs b/tests/debuginfo/option-like-enum.rs index d370796efa9d2..72a41986dcef0 100644 --- a/tests/debuginfo/option-like-enum.rs +++ b/tests/debuginfo/option-like-enum.rs @@ -8,22 +8,22 @@ // gdb-command:run // gdb-command:print some -// gdb-check:$1 = core::option::Option<&u32>::Some(0x12345678) +// gdb-check:$1 = core::option::Option<&u32>::Some(0x[...]) // gdb-command:print none // gdb-check:$2 = core::option::Option<&u32>::None // gdb-command:print full -// gdb-check:$3 = option_like_enum::MoreFields::Full(454545, 0x87654321, 9988) +// gdb-check:$3 = option_like_enum::MoreFields::Full(454545, 0x[...], 9988) -// gdb-command:print empty_gdb.discr -// gdb-check:$4 = (*mut isize) 0x1 +// gdb-command:print empty +// gdb-check:$4 = option_like_enum::MoreFields::Empty // gdb-command:print droid -// gdb-check:$5 = option_like_enum::NamedFields::Droid{id: 675675, range: 10000001, internals: 0x43218765} +// gdb-check:$5 = option_like_enum::NamedFields::Droid{id: 675675, range: 10000001, internals: 0x[...]} -// gdb-command:print void_droid_gdb.internals -// gdb-check:$6 = (*mut isize) 0x1 +// gdb-command:print void_droid +// gdb-check:$6 = option_like_enum::NamedFields::Void // gdb-command:print nested_non_zero_yep // gdb-check:$7 = option_like_enum::NestedNonZero::Yep(10.5, option_like_enum::NestedNonZeroField {a: 10, b: 20, c: 0x[...]}) @@ -39,19 +39,19 @@ // lldb-command:run // lldb-command:v some -// lldb-check:[...] Some(&0x12345678) +// lldb-check:[...] Some(&0x[...]) // lldb-command:v none // lldb-check:[...] None // lldb-command:v full -// lldb-check:[...] Full(454545, &0x87654321, 9988) +// lldb-check:[...] Full(454545, &0x[...], 9988) // lldb-command:v empty // lldb-check:[...] Empty // lldb-command:v droid -// lldb-check:[...] Droid { id: 675675, range: 10000001, internals: &0x43218765 } +// lldb-check:[...] Droid { id: 675675, range: 10000001, internals: &0x[...] } // lldb-command:v void_droid // lldb-check:[...] Void @@ -76,11 +76,6 @@ // contains a non-nullable pointer, then this value is used as the discriminator. // The test cases in this file make sure that something readable is generated for // this kind of types. -// If the non-empty variant contains a single non-nullable pointer than the whole -// item is represented as just a pointer and not wrapped in a struct. -// Unfortunately (for these test cases) the content of the non-discriminant fields -// in the null-case is not defined. So we just read the discriminator field in -// this case (by casting the value to a memory-equivalent struct). enum MoreFields<'a> { Full(u32, &'a isize, i16), @@ -120,32 +115,26 @@ fn main() { let some_str: Option<&'static str> = Some("abc"); let none_str: Option<&'static str> = None; - let some: Option<&u32> = Some(unsafe { std::mem::transmute(0x12345678_usize) }); + let some: Option<&u32> = Some(&1234); let none: Option<&u32> = None; - let full = MoreFields::Full(454545, unsafe { std::mem::transmute(0x87654321_usize) }, 9988); - + let full = MoreFields::Full(454545, &1234, 9988); let empty = MoreFields::Empty; - let empty_gdb: &MoreFieldsRepr = unsafe { std::mem::transmute(&MoreFields::Empty) }; let droid = NamedFields::Droid { id: 675675, range: 10000001, - internals: unsafe { std::mem::transmute(0x43218765_usize) } + internals: &1234, }; - let void_droid = NamedFields::Void; - let void_droid_gdb: &NamedFieldsRepr = unsafe { std::mem::transmute(&NamedFields::Void) }; - let x = 'x'; let nested_non_zero_yep = NestedNonZero::Yep( 10.5, NestedNonZeroField { a: 10, b: 20, - c: &x + c: &'x', }); - let nested_non_zero_nope = NestedNonZero::Nope; zzz(); // #break From ea6df5c147537029cb6d142abcbcb02675a03740 Mon Sep 17 00:00:00 2001 From: Rain Date: Mon, 26 Aug 2024 11:00:24 -0700 Subject: [PATCH 06/12] Update stacker to 0.1.17 The main new feature is support for detecting the current stack size on illumos. (See my blog post [1] for the context which led to this.) [1]: https://sunshowers.io/posts/rustc-segfault-illumos/ --- Cargo.lock | 6 +++--- compiler/rustc_data_structures/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ea4cd8f5aca4..207117933443c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4978,15 +4978,15 @@ dependencies = [ [[package]] name = "stacker" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi", + "windows-sys 0.59.0", ] [[package]] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 69cbf8c416105..38c6c8a8d11c7 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -22,7 +22,7 @@ rustc_index = { path = "../rustc_index", package = "rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] } -stacker = "0.1.15" +stacker = "0.1.17" tempfile = "3.2" thin-vec = "0.2.12" tracing = "0.1" From fff063ee77bad38ee908577e0f40c442e0ab97a3 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Fri, 30 Aug 2024 12:13:33 +0200 Subject: [PATCH 07/12] add crashtests for several old unfixed ICEs --- tests/crashes/117460.rs | 8 +++++++ tests/crashes/119095.rs | 48 +++++++++++++++++++++++++++++++++++++++++ tests/crashes/126443.rs | 15 +++++++++++++ tests/crashes/128097.rs | 6 ++++++ 4 files changed, 77 insertions(+) create mode 100644 tests/crashes/117460.rs create mode 100644 tests/crashes/119095.rs create mode 100644 tests/crashes/126443.rs create mode 100644 tests/crashes/128097.rs diff --git a/tests/crashes/117460.rs b/tests/crashes/117460.rs new file mode 100644 index 0000000000000..4878a35ffe5fb --- /dev/null +++ b/tests/crashes/117460.rs @@ -0,0 +1,8 @@ +//@ known-bug: #117460 +#![feature(generic_const_exprs)] + +struct Matrix { + d: D, +} + +impl Matrix {} diff --git a/tests/crashes/119095.rs b/tests/crashes/119095.rs new file mode 100644 index 0000000000000..28742e0d5daea --- /dev/null +++ b/tests/crashes/119095.rs @@ -0,0 +1,48 @@ +//@ known-bug: #119095 +//@ compile-flags: --edition=2021 + +fn any() -> T { + loop {} +} + +trait Acquire { + type Connection; +} + +impl Acquire for &'static () { + type Connection = (); +} + +trait Unit {} +impl Unit for () {} + +fn get_connection() -> impl Unit +where + T: Acquire, + T::Connection: Unit, +{ + any::() +} + +fn main() { + let future = async { async { get_connection::<&'static ()>() }.await }; + + future.resolve_me(); +} + +trait ResolveMe { + fn resolve_me(self); +} + +impl ResolveMe for S +where + (): CheckSend, +{ + fn resolve_me(self) {} +} + +trait CheckSend {} +impl CheckSend for () where F: Send {} + +trait NeverImplemented {} +impl CheckSend for E where E: NeverImplemented {} diff --git a/tests/crashes/126443.rs b/tests/crashes/126443.rs new file mode 100644 index 0000000000000..fba779444f94c --- /dev/null +++ b/tests/crashes/126443.rs @@ -0,0 +1,15 @@ +//@ known-bug: #126443 +//@ compile-flags: -Copt-level=0 +#![feature(generic_const_exprs)] + +fn double_up() -> [(); M * 2] { + todo!() +} + +fn quadruple_up() -> [(); N * 2 * 2] { + double_up() +} + +fn main() { + quadruple_up::<0>(); +} diff --git a/tests/crashes/128097.rs b/tests/crashes/128097.rs new file mode 100644 index 0000000000000..6ffca640cbd6b --- /dev/null +++ b/tests/crashes/128097.rs @@ -0,0 +1,6 @@ +//@ known-bug: #128097 +#![feature(explicit_tail_calls)] +fn f(x: &mut ()) { + let _y: String; + become f(x); +} From 5d044724611983620c9c1ce52c6eef80be28633d Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 15 Aug 2024 18:05:59 +0300 Subject: [PATCH 08/12] Implement `elided_named_lifetimes` lint --- compiler/rustc_ast_lowering/src/lib.rs | 6 +- .../src/lifetime_collector.rs | 2 +- compiler/rustc_hir/src/def.rs | 9 ++- compiler/rustc_lint/messages.ftl | 4 + .../rustc_lint/src/context/diagnostics.rs | 13 +++ compiler/rustc_lint/src/lints.rs | 10 +++ compiler/rustc_lint_defs/src/builtin.rs | 33 ++++++++ compiler/rustc_lint_defs/src/lib.rs | 7 ++ compiler/rustc_resolve/src/late.rs | 79 ++++++++++++++++--- .../rustc_resolve/src/late/diagnostics.rs | 11 ++- 10 files changed, 154 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bcc2c29a2ff79..9b46e7c46b666 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -837,7 +837,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind)) } - LifetimeRes::Static | LifetimeRes::Error => return None, + LifetimeRes::Static { .. } | LifetimeRes::Error => return None, res => panic!( "Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, ident.span @@ -1656,7 +1656,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } // Opaques do not capture `'static` - LifetimeRes::Static | LifetimeRes::Error => { + LifetimeRes::Static { .. } | LifetimeRes::Error => { continue; } @@ -2069,7 +2069,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::LifetimeName::Param(param) } LifetimeRes::Infer => hir::LifetimeName::Infer, - LifetimeRes::Static => hir::LifetimeName::Static, + LifetimeRes::Static { .. } => hir::LifetimeName::Static, LifetimeRes::Error => hir::LifetimeName::Error, res => panic!( "Unexpected lifetime resolution {:?} for {:?} at {:?}", diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index 77cc2a36a531d..76c957afa5461 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -27,7 +27,7 @@ impl<'ast> LifetimeCollectVisitor<'ast> { self.collected_lifetimes.insert(lifetime); } } - LifetimeRes::Static | LifetimeRes::Error => { + LifetimeRes::Static { .. } | LifetimeRes::Error => { self.collected_lifetimes.insert(lifetime); } LifetimeRes::Infer => {} diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 36e29d2dcb2cc..c5dc4dacab650 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -863,8 +863,13 @@ pub enum LifetimeRes { /// This variant is used for anonymous lifetimes that we did not resolve during /// late resolution. Those lifetimes will be inferred by typechecking. Infer, - /// Explicit `'static` lifetime. - Static, + /// `'static` lifetime. + Static { + /// We do not want to emit `elided_named_lifetimes` + /// when we are inside of a const item or a static, + /// because it would get too annoying. + suppress_elision_warning: bool, + }, /// Resolution failure. Error, /// HACK: This is used to recover the NodeId of an elided lifetime. diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 08a50050a36d5..31b7eb5ee7df3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -252,6 +252,10 @@ lint_duplicate_macro_attribute = lint_duplicate_matcher_binding = duplicate matcher binding +lint_elided_named_lifetime = elided lifetime has a name + .label_elided = this elided lifetime gets resolved as `{$name}` + .label_named = lifetime `{$name}` declared here + lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index f289d4c81b3c1..fd43afa174383 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -10,6 +10,7 @@ use rustc_errors::{ use rustc_middle::middle::stability; use rustc_session::lint::BuiltinLintDiag; use rustc_session::Session; +use rustc_span::symbol::kw; use rustc_span::BytePos; use tracing::debug; @@ -441,5 +442,17 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) } + BuiltinLintDiag::ElidedIsStatic { elided } => { + lints::ElidedNamedLifetime { elided, name: kw::StaticLifetime, named_declaration: None } + .decorate_lint(diag) + } + BuiltinLintDiag::ElidedIsParam { elided, param: (param_name, param_span) } => { + lints::ElidedNamedLifetime { + elided, + name: param_name, + named_declaration: Some(param_span), + } + .decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c6bcb1f3e8334..2712e25668a48 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2611,6 +2611,16 @@ pub(crate) struct ElidedLifetimesInPaths { pub subdiag: ElidedLifetimeInPathSubdiag, } +#[derive(LintDiagnostic)] +#[diag(lint_elided_named_lifetime)] +pub(crate) struct ElidedNamedLifetime { + #[label(lint_label_elided)] + pub elided: Span, + pub name: Symbol, + #[label(lint_label_named)] + pub named_declaration: Option, +} + #[derive(LintDiagnostic)] #[diag(lint_invalid_crate_type_value)] pub(crate) struct UnknownCrateTypes { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 44c72e0c4fe33..3e8b180e78b78 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -42,6 +42,7 @@ declare_lint_pass! { DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, + ELIDED_NAMED_LIFETIMES, EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, @@ -1862,6 +1863,38 @@ declare_lint! { "hidden lifetime parameters in types are deprecated" } +declare_lint! { + /// The `elided_named_lifetimes` lint detects when an elided + /// lifetime ends up being a named lifetime, such as `'static` + /// or some lifetime parameter `'a`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(elided_named_lifetimes)] + /// struct Foo; + /// impl Foo { + /// pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { + /// unsafe { &mut *(x as *mut _) } + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Lifetime elision is quite useful, because it frees you from having + /// to give each lifetime its own name, but sometimes it can produce + /// somewhat surprising resolutions. In safe code, it is mostly okay, + /// because the borrow checker prevents any unsoundness, so the worst + /// case scenario is you get a confusing error message in some other place. + /// But with `unsafe` code, such unexpected resolutions may lead to unsound code. + pub ELIDED_NAMED_LIFETIMES, + Warn, + "detects when an elided lifetime gets resolved to be `'static` or some named parameter" +} + declare_lint! { /// The `bare_trait_objects` lint suggests using `dyn Trait` for trait /// objects. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index c17b85db3b044..8ddbff284cc2e 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -589,6 +589,13 @@ pub enum BuiltinLintDiag { }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), + ElidedIsStatic { + elided: Span, + }, + ElidedIsParam { + elided: Span, + param: (Symbol, Span), + }, UnknownCrateTypes { span: Span, candidate: Option, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 40fdb01a72c0e..8ec0469ed6d87 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1557,14 +1557,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if ident.name == kw::StaticLifetime { self.record_lifetime_res( lifetime.id, - LifetimeRes::Static, + LifetimeRes::Static { suppress_elision_warning: false }, LifetimeElisionCandidate::Named, ); return; } if ident.name == kw::UnderscoreLifetime { - return self.resolve_anonymous_lifetime(lifetime, false); + return self.resolve_anonymous_lifetime(lifetime, lifetime.id, false); } let mut lifetime_rib_iter = self.lifetime_ribs.iter().rev(); @@ -1667,13 +1667,23 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { + fn resolve_anonymous_lifetime( + &mut self, + lifetime: &Lifetime, + id_for_lint: NodeId, + elided: bool, + ) { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); let kind = if elided { MissingLifetimeKind::Ampersand } else { MissingLifetimeKind::Underscore }; - let missing_lifetime = - MissingLifetime { id: lifetime.id, span: lifetime.ident.span, kind, count: 1 }; + let missing_lifetime = MissingLifetime { + id: lifetime.id, + span: lifetime.ident.span, + kind, + count: 1, + id_for_lint, + }; let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() { debug!(?rib.kind); @@ -1697,7 +1707,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if lifetimes_in_scope.is_empty() { self.record_lifetime_res( lifetime.id, - LifetimeRes::Static, + // We are inside a const item, so do not warn. + LifetimeRes::Static { suppress_elision_warning: true }, elision_candidate, ); return; @@ -1800,7 +1811,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) }, LifetimeElisionCandidate::Ignore, ); - self.resolve_anonymous_lifetime(<, true); + self.resolve_anonymous_lifetime(<, anchor_id, true); } #[instrument(level = "debug", skip(self))] @@ -1916,6 +1927,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }; let missing_lifetime = MissingLifetime { id: node_ids.start, + id_for_lint: segment_id, span: elided_lifetime_span, kind, count: expected_lifetimes, @@ -2039,8 +2051,44 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { panic!("lifetime {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)") } + + match candidate { + LifetimeElisionCandidate::Missing(missing @ MissingLifetime { .. }) => { + debug_assert_eq!(id, missing.id); + match res { + LifetimeRes::Static { suppress_elision_warning } => { + if !suppress_elision_warning { + self.r.lint_buffer.buffer_lint( + lint::builtin::ELIDED_NAMED_LIFETIMES, + missing.id_for_lint, + missing.span, + BuiltinLintDiag::ElidedIsStatic { elided: missing.span }, + ); + } + } + LifetimeRes::Param { param, binder: _ } => { + let tcx = self.r.tcx(); + self.r.lint_buffer.buffer_lint( + lint::builtin::ELIDED_NAMED_LIFETIMES, + missing.id_for_lint, + missing.span, + BuiltinLintDiag::ElidedIsParam { + elided: missing.span, + param: (tcx.item_name(param.into()), tcx.source_span(param)), + }, + ); + } + LifetimeRes::Fresh { .. } + | LifetimeRes::Infer + | LifetimeRes::Error + | LifetimeRes::ElidedAnchor { .. } => {} + } + } + LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => {} + } + match res { - LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => { + LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static { .. } => { if let Some(ref mut candidates) = self.lifetime_elision_candidates { candidates.push((res, candidate)); } @@ -2558,9 +2606,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. }) => { self.with_static_rib(def_kind, |this| { - this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { - this.visit_ty(ty); - }); + this.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Static { + suppress_elision_warning: true, + }), + |this| { + this.visit_ty(ty); + }, + ); if let Some(expr) = expr { // We already forbid generic params because of the above item rib, // so it doesn't matter whether this is a trivial constant. @@ -2589,7 +2642,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { this.visit_generics(generics); this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Static), + LifetimeRibKind::Elided(LifetimeRes::Static { + suppress_elision_warning: true, + }), |this| this.visit_ty(ty), ); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f778b0ee3acc0..8f516c2db0900 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -102,6 +102,13 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str pub(super) struct MissingLifetime { /// Used to overwrite the resolution with the suggestion, to avoid cascading errors. pub id: NodeId, + /// As we cannot yet emit lints in this crate and have to buffer them instead, + /// we need to associate each lint with some `NodeId`, + /// however for some `MissingLifetime`s their `NodeId`s are "fake", + /// in a sense that they are temporary and not get preserved down the line, + /// which means that the lints for those nodes will not get emitted. + /// To combat this, we can try to use some other `NodeId`s as a fallback option. + pub id_for_lint: NodeId, /// Where to suggest adding the lifetime. pub span: Span, /// How the lifetime was introduced, to have the correct space and comma. @@ -3028,7 +3035,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { maybe_static = true; in_scope_lifetimes = vec![( Ident::with_dummy_span(kw::StaticLifetime), - (DUMMY_NODE_ID, LifetimeRes::Static), + (DUMMY_NODE_ID, LifetimeRes::Static { suppress_elision_warning: false }), )]; } } else if elided_len == 0 { @@ -3040,7 +3047,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { maybe_static = true; in_scope_lifetimes = vec![( Ident::with_dummy_span(kw::StaticLifetime), - (DUMMY_NODE_ID, LifetimeRes::Static), + (DUMMY_NODE_ID, LifetimeRes::Static { suppress_elision_warning: false }), )]; } } else if num_params == 1 { From f7b0b221373e7de22191fccbced9942a21af924f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sat, 17 Aug 2024 23:47:37 +0300 Subject: [PATCH 09/12] Fix `elided_named_lifetimes` in code --- library/alloc/src/string.rs | 2 +- library/core/src/fmt/rt.rs | 20 +++++++++---------- .../rust-analyzer/crates/hir-def/src/attr.rs | 4 ++-- .../rust-analyzer/crates/hir-def/src/body.rs | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index e628be1546f76..bc8b7e24bf12b 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2313,7 +2313,7 @@ impl<'b> Pattern for &'b String { } #[inline] - fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&str> + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> where Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, { diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index f29ac99b29255..eee4a9e4c6c89 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -110,43 +110,43 @@ impl<'a> Argument<'a> { } #[inline(always)] - pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> { + pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'b> { Self::new(x, Display::fmt) } #[inline(always)] - pub fn new_debug<'b, T: Debug>(x: &'b T) -> Argument<'_> { + pub fn new_debug<'b, T: Debug>(x: &'b T) -> Argument<'b> { Self::new(x, Debug::fmt) } #[inline(always)] - pub fn new_debug_noop<'b, T: Debug>(x: &'b T) -> Argument<'_> { + pub fn new_debug_noop<'b, T: Debug>(x: &'b T) -> Argument<'b> { Self::new(x, |_, _| Ok(())) } #[inline(always)] - pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'_> { + pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'b> { Self::new(x, Octal::fmt) } #[inline(always)] - pub fn new_lower_hex<'b, T: LowerHex>(x: &'b T) -> Argument<'_> { + pub fn new_lower_hex<'b, T: LowerHex>(x: &'b T) -> Argument<'b> { Self::new(x, LowerHex::fmt) } #[inline(always)] - pub fn new_upper_hex<'b, T: UpperHex>(x: &'b T) -> Argument<'_> { + pub fn new_upper_hex<'b, T: UpperHex>(x: &'b T) -> Argument<'b> { Self::new(x, UpperHex::fmt) } #[inline(always)] - pub fn new_pointer<'b, T: Pointer>(x: &'b T) -> Argument<'_> { + pub fn new_pointer<'b, T: Pointer>(x: &'b T) -> Argument<'b> { Self::new(x, Pointer::fmt) } #[inline(always)] - pub fn new_binary<'b, T: Binary>(x: &'b T) -> Argument<'_> { + pub fn new_binary<'b, T: Binary>(x: &'b T) -> Argument<'b> { Self::new(x, Binary::fmt) } #[inline(always)] - pub fn new_lower_exp<'b, T: LowerExp>(x: &'b T) -> Argument<'_> { + pub fn new_lower_exp<'b, T: LowerExp>(x: &'b T) -> Argument<'b> { Self::new(x, LowerExp::fmt) } #[inline(always)] - pub fn new_upper_exp<'b, T: UpperExp>(x: &'b T) -> Argument<'_> { + pub fn new_upper_exp<'b, T: UpperExp>(x: &'b T) -> Argument<'b> { Self::new(x, UpperExp::fmt) } #[inline(always)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 198dc93f6b141..c0547bc3c8d12 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -117,7 +117,7 @@ impl Attrs { } impl Attrs { - pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'_> { + pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'attrs> { AttrQuery { attrs: self, key } } @@ -594,7 +594,7 @@ impl<'attr> AttrQuery<'attr> { /// #[doc(html_root_url = "url")] /// ^^^^^^^^^^^^^ key /// ``` - pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&str> { + pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&'attr str> { self.tt_values().find_map(|tt| { let name = tt.token_trees.iter() .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key)) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index a988317e046ef..d390a9cd2d747 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -197,7 +197,7 @@ impl Body { pub fn blocks<'a>( &'a self, db: &'a dyn DefDatabase, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + 'a { self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block))) } From a9b959a020cc156ef57a972c83381aabc43b1174 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sat, 31 Aug 2024 15:31:31 +0300 Subject: [PATCH 10/12] elided_named_lifetimes: bless & add tests --- .../clippy/tests/ui/needless_lifetimes.stderr | 13 +++++- src/tools/clippy/tests/ui/ptr_arg.stderr | 11 ++++- tests/ui/async-await/issues/issue-63388-1.rs | 2 +- .../async-await/issues/issue-63388-1.stderr | 13 +++++- .../type-dependent/issue-71348.full.stderr | 10 ++++ .../type-dependent/issue-71348.min.stderr | 10 +++- .../type-dependent/issue-71348.rs | 1 + tests/ui/consts/min_const_fn/min_const_fn.rs | 4 +- .../consts/min_const_fn/min_const_fn.stderr | 22 ++++++++- tests/ui/generics/generic-no-mangle.fixed | 2 +- tests/ui/generics/generic-no-mangle.rs | 2 +- tests/ui/impl-trait/impl-fn-hrtb-bounds.rs | 1 + .../ui/impl-trait/impl-fn-hrtb-bounds.stderr | 12 ++++- .../impl-fn-predefined-lifetimes.rs | 1 + .../impl-fn-predefined-lifetimes.stderr | 14 ++++-- .../rpit-assoc-pair-with-lifetime.rs | 1 + .../rpit-assoc-pair-with-lifetime.stderr | 10 ++++ ...-return-type-requires-explicit-lifetime.rs | 1 + ...urn-type-requires-explicit-lifetime.stderr | 12 ++++- ...-one-existing-name-if-else-using-impl-3.rs | 1 + ...-existing-name-if-else-using-impl-3.stderr | 16 +++++-- .../example-from-issue48686.rs | 12 +++++ .../example-from-issue48686.stderr | 14 ++++++ .../missing-lifetime-kind.rs | 27 +++++++++++ .../missing-lifetime-kind.stderr | 40 ++++++++++++++++ .../not-tied-to-crate.rs | 17 +++++++ .../not-tied-to-crate.stderr | 26 +++++++++++ .../ui/lint/elided-named-lifetimes/static.rs | 46 +++++++++++++++++++ .../lint/elided-named-lifetimes/static.stderr | 32 +++++++++++++ .../object-lifetime-default-elision.rs | 2 + .../object-lifetime-default-elision.stderr | 14 +++++- .../elision/ignore-non-reference-lifetimes.rs | 2 + .../ignore-non-reference-lifetimes.stderr | 16 +++++++ tests/ui/self/elision/lt-ref-self-async.fixed | 2 +- tests/ui/self/elision/lt-ref-self-async.rs | 2 +- tests/ui/self/self_lifetime-async.rs | 2 + tests/ui/self/self_lifetime-async.stderr | 18 ++++++++ tests/ui/self/self_lifetime.rs | 2 + tests/ui/self/self_lifetime.stderr | 18 ++++++++ .../impl-trait-missing-lifetime-gated.rs | 1 + .../impl-trait-missing-lifetime-gated.stderr | 10 +++- .../missing-lifetimes-in-signature.rs | 1 + .../missing-lifetimes-in-signature.stderr | 10 +++- .../missing_lifetime_bound.rs | 2 +- .../missing_lifetime_bound.stderr | 12 ++++- 45 files changed, 460 insertions(+), 27 deletions(-) create mode 100644 tests/ui/const-generics/type-dependent/issue-71348.full.stderr create mode 100644 tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr create mode 100644 tests/ui/lint/elided-named-lifetimes/example-from-issue48686.rs create mode 100644 tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr create mode 100644 tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs create mode 100644 tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr create mode 100644 tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs create mode 100644 tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr create mode 100644 tests/ui/lint/elided-named-lifetimes/static.rs create mode 100644 tests/ui/lint/elided-named-lifetimes/static.stderr create mode 100644 tests/ui/self/elision/ignore-non-reference-lifetimes.stderr create mode 100644 tests/ui/self/self_lifetime-async.stderr create mode 100644 tests/ui/self/self_lifetime.stderr diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr index f325d27b8c753..50f845e2d9291 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -1,3 +1,14 @@ +error: elided lifetime has a name + --> tests/ui/needless_lifetimes.rs:266:52 + | +LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + | + = note: `-D elided-named-lifetimes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` + error: the following explicit lifetimes could be elided: 'a, 'b --> tests/ui/needless_lifetimes.rs:17:23 | @@ -553,5 +564,5 @@ LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { LL + fn one_input(x: &u8) -> &u8 { | -error: aborting due to 46 previous errors +error: aborting due to 47 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 1848ef80fc495..4246453e64ca7 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -1,3 +1,12 @@ +error: elided lifetime has a name + --> tests/ui/ptr_arg.rs:295:56 + | +LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str { + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | + = note: `-D elided-named-lifetimes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` + error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do --> tests/ui/ptr_arg.rs:13:14 | @@ -212,5 +221,5 @@ error: using a reference to `Cow` is not recommended LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` -error: aborting due to 24 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/async-await/issues/issue-63388-1.rs b/tests/ui/async-await/issues/issue-63388-1.rs index 32026a22a1616..a6f499ba94e29 100644 --- a/tests/ui/async-await/issues/issue-63388-1.rs +++ b/tests/ui/async-await/issues/issue-63388-1.rs @@ -9,7 +9,7 @@ trait Foo {} impl Xyz { async fn do_sth<'a>( &'a self, foo: &dyn Foo - ) -> &dyn Foo + ) -> &dyn Foo //~ WARNING elided lifetime has a name { //~^ ERROR explicit lifetime required in the type of `foo` [E0621] foo diff --git a/tests/ui/async-await/issues/issue-63388-1.stderr b/tests/ui/async-await/issues/issue-63388-1.stderr index f7f285ad0ccda..ef74bfe32375e 100644 --- a/tests/ui/async-await/issues/issue-63388-1.stderr +++ b/tests/ui/async-await/issues/issue-63388-1.stderr @@ -1,3 +1,14 @@ +warning: elided lifetime has a name + --> $DIR/issue-63388-1.rs:12:10 + | +LL | async fn do_sth<'a>( + | -- lifetime `'a` declared here +LL | &'a self, foo: &dyn Foo +LL | ) -> &dyn Foo + | ^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error[E0621]: explicit lifetime required in the type of `foo` --> $DIR/issue-63388-1.rs:13:5 | @@ -10,6 +21,6 @@ LL | | foo LL | | } | |_____^ lifetime `'a` required -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0621`. diff --git a/tests/ui/const-generics/type-dependent/issue-71348.full.stderr b/tests/ui/const-generics/type-dependent/issue-71348.full.stderr new file mode 100644 index 0000000000000..177ff20fbf9ea --- /dev/null +++ b/tests/ui/const-generics/type-dependent/issue-71348.full.stderr @@ -0,0 +1,10 @@ +warning: elided lifetime has a name + --> $DIR/issue-71348.rs:18:68 + | +LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr index 858900a500d84..5aee282952aa2 100644 --- a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr +++ b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr @@ -1,3 +1,11 @@ +warning: elided lifetime has a name + --> $DIR/issue-71348.rs:18:68 + | +LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error: `&'static str` is forbidden as the type of a const generic parameter --> $DIR/issue-71348.rs:10:24 | @@ -30,5 +38,5 @@ help: add `#![feature(unsized_const_params)]` to the crate attributes to enable LL + #![feature(unsized_const_params)] | -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/const-generics/type-dependent/issue-71348.rs b/tests/ui/const-generics/type-dependent/issue-71348.rs index 2ffbd015485aa..97e786405fe39 100644 --- a/tests/ui/const-generics/type-dependent/issue-71348.rs +++ b/tests/ui/const-generics/type-dependent/issue-71348.rs @@ -17,6 +17,7 @@ trait Get<'a, const N: &'static str> { impl Foo { fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target //[min]~^ ERROR `&'static str` is forbidden as the type of a const generic parameter + //~^^ WARNING elided lifetime has a name where Self: Get<'a, N>, { diff --git a/tests/ui/consts/min_const_fn/min_const_fn.rs b/tests/ui/consts/min_const_fn/min_const_fn.rs index 76245c08ffc8c..f7663f6044eef 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn.rs +++ b/tests/ui/consts/min_const_fn/min_const_fn.rs @@ -44,8 +44,8 @@ impl Foo { impl<'a, T> Foo { const fn new_lt(t: T) -> Self { Foo(t) } const fn into_inner_lt(self) -> T { self.0 } //~ destructor of - const fn get_lt(&'a self) -> &T { &self.0 } - const fn get_mut_lt(&'a mut self) -> &mut T { &mut self.0 } + const fn get_lt(&'a self) -> &T { &self.0 } //~ WARNING elided lifetime has a name + const fn get_mut_lt(&'a mut self) -> &mut T { &mut self.0 } //~ WARNING elided lifetime has a name //~^ mutable references //~| mutable references //~| mutable references diff --git a/tests/ui/consts/min_const_fn/min_const_fn.stderr b/tests/ui/consts/min_const_fn/min_const_fn.stderr index daa0ab2614fb1..4b348a182b87f 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn.stderr +++ b/tests/ui/consts/min_const_fn/min_const_fn.stderr @@ -1,3 +1,23 @@ +warning: elided lifetime has a name + --> $DIR/min_const_fn.rs:47:34 + | +LL | impl<'a, T> Foo { + | -- lifetime `'a` declared here +... +LL | const fn get_lt(&'a self) -> &T { &self.0 } + | ^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + +warning: elided lifetime has a name + --> $DIR/min_const_fn.rs:48:42 + | +LL | impl<'a, T> Foo { + | -- lifetime `'a` declared here +... +LL | const fn get_mut_lt(&'a mut self) -> &mut T { &mut self.0 } + | ^ this elided lifetime gets resolved as `'a` + error[E0493]: destructor of `Foo` cannot be evaluated at compile-time --> $DIR/min_const_fn.rs:37:25 | @@ -228,7 +248,7 @@ LL | const fn no_apit(_x: impl std::fmt::Debug) {} | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 24 previous errors +error: aborting due to 24 previous errors; 2 warnings emitted Some errors have detailed explanations: E0493, E0658. For more information about an error, try `rustc --explain E0493`. diff --git a/tests/ui/generics/generic-no-mangle.fixed b/tests/ui/generics/generic-no-mangle.fixed index 69db712f9dc65..2776848c45fff 100644 --- a/tests/ui/generics/generic-no-mangle.fixed +++ b/tests/ui/generics/generic-no-mangle.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -#![allow(dead_code)] +#![allow(dead_code, elided_named_lifetimes)] #![deny(no_mangle_generic_items)] pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled diff --git a/tests/ui/generics/generic-no-mangle.rs b/tests/ui/generics/generic-no-mangle.rs index 2288b5bbe70df..5314005d31fa1 100644 --- a/tests/ui/generics/generic-no-mangle.rs +++ b/tests/ui/generics/generic-no-mangle.rs @@ -1,5 +1,5 @@ //@ run-rustfix -#![allow(dead_code)] +#![allow(dead_code, elided_named_lifetimes)] #![deny(no_mangle_generic_items)] #[no_mangle] diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs index da7530b4e7a8c..a7f38b5c16af2 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs @@ -13,6 +13,7 @@ fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` + //~| WARNING elided lifetime has a name |x| x } diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr index 7d108b30b76ed..d0f8f7689d17c 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/impl-fn-hrtb-bounds.rs:19:38 + --> $DIR/impl-fn-hrtb-bounds.rs:20:38 | LL | fn d() -> impl Fn() -> (impl Debug + '_) { | ^^ expected named lifetime parameter @@ -10,6 +10,14 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | fn d() -> impl Fn() -> (impl Debug + 'static) { | ~~~~~~~ +warning: elided lifetime has a name + --> $DIR/impl-fn-hrtb-bounds.rs:14:52 + | +LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { + | -- lifetime `'a` declared here ^^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` --> $DIR/impl-fn-hrtb-bounds.rs:4:41 | @@ -46,7 +54,7 @@ note: lifetime declared here LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { | ^^ -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted Some errors have detailed explanations: E0106, E0657. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs index 8aba3de530b8e..2f17c0ff50861 100644 --- a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs +++ b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { //~^ ERROR cannot resolve opaque type + //~| WARNING elided lifetime has a name |x| x //~^ ERROR expected generic lifetime parameter, found `'_` } diff --git a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr index c2386e8c88be0..50a9f3ebeabb8 100644 --- a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr +++ b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr @@ -1,9 +1,17 @@ +warning: elided lifetime has a name + --> $DIR/impl-fn-predefined-lifetimes.rs:4:48 + | +LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { + | -- lifetime `'a` declared here ^^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/impl-fn-predefined-lifetimes.rs:6:9 + --> $DIR/impl-fn-predefined-lifetimes.rs:7:9 | LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { | -- this generic parameter must be used with a generic lifetime parameter -LL | +... LL | |x| x | ^ @@ -13,7 +21,7 @@ error[E0720]: cannot resolve opaque type LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { | ^^^^^^^^^^^^^^^ cannot resolve opaque type -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted Some errors have detailed explanations: E0720, E0792. For more information about an error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs index 73c8a6c0aed9f..e48441f533d7c 100644 --- a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs +++ b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs @@ -1,6 +1,7 @@ //@ check-pass pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator { + //~^ WARNING elided lifetime has a name v.into_iter() } diff --git a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr new file mode 100644 index 0000000000000..bff3ffd934ac6 --- /dev/null +++ b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr @@ -0,0 +1,10 @@ +warning: elided lifetime has a name + --> $DIR/rpit-assoc-pair-with-lifetime.rs:3:82 + | +LL | pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator { + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs index d0a8fe795efd0..63a2c9be9ebad 100644 --- a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -47,5 +47,6 @@ fn l<'a>(_: &'a str, _: &'a str) -> &str { "" } // This is ok because both `'a` are for the same parameter. fn m<'a>(_: &'a Foo<'a>) -> &str { "" } +//~^ WARNING elided lifetime has a name fn main() {} diff --git a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr index 23ef36888f079..f835d2655bb01 100644 --- a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr +++ b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr @@ -105,6 +105,16 @@ help: consider using the `'a` lifetime LL | fn l<'a>(_: &'a str, _: &'a str) -> &'a str { "" } | ++ -error: aborting due to 7 previous errors +warning: elided lifetime has a name + --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:49:29 + | +LL | fn m<'a>(_: &'a Foo<'a>) -> &str { "" } + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + | + = note: `#[warn(elided_named_lifetimes)]` on by default + +error: aborting due to 7 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs index a1126d6bb1543..598633d75768e 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs @@ -4,6 +4,7 @@ struct Foo { impl Foo { fn foo<'a>(&'a self, x: &i32) -> &i32 { + //~^ WARNING elided lifetime has a name if true { &self.field } else { x } //~ ERROR explicit lifetime diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr index 6dda9e61a79ca..2d5d4fb0e72ec 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr @@ -1,12 +1,22 @@ +warning: elided lifetime has a name + --> $DIR/ex1-return-one-existing-name-if-else-using-impl-3.rs:6:36 + | +LL | fn foo<'a>(&'a self, x: &i32) -> &i32 { + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/ex1-return-one-existing-name-if-else-using-impl-3.rs:8:36 + --> $DIR/ex1-return-one-existing-name-if-else-using-impl-3.rs:9:36 | LL | fn foo<'a>(&'a self, x: &i32) -> &i32 { | ---- help: add explicit lifetime `'a` to the type of `x`: `&'a i32` -LL | +... LL | if true { &self.field } else { x } | ^ lifetime `'a` required -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0621`. diff --git a/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.rs b/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.rs new file mode 100644 index 0000000000000..eac7c32a9aac6 --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.rs @@ -0,0 +1,12 @@ +#![deny(elided_named_lifetimes)] + +struct Foo; + +impl Foo { + pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { + //~^ ERROR elided lifetime has a name + unsafe { &mut *(x as *mut _) } + } +} + +fn main() {} diff --git a/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr b/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr new file mode 100644 index 0000000000000..8c5426a60cb72 --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr @@ -0,0 +1,14 @@ +error: elided lifetime has a name + --> $DIR/example-from-issue48686.rs:6:50 + | +LL | pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { + | ^ this elided lifetime gets resolved as `'static` + | +note: the lint level is defined here + --> $DIR/example-from-issue48686.rs:1:9 + | +LL | #![deny(elided_named_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs b/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs new file mode 100644 index 0000000000000..2f9083ed65f6e --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs @@ -0,0 +1,27 @@ +#![deny(elided_named_lifetimes)] + +fn ampersand<'a>(x: &'a u8) -> &u8 { + //~^ ERROR elided lifetime has a name + x +} + +struct Brackets<'a>(&'a u8); + +fn brackets<'a>(x: &'a u8) -> Brackets { + //~^ ERROR elided lifetime has a name + Brackets(x) +} + +struct Comma<'a, T>(&'a T); + +fn comma<'a>(x: &'a u8) -> Comma { + //~^ ERROR elided lifetime has a name + Comma(x) +} + +fn underscore<'a>(x: &'a u8) -> &'_ u8 { + //~^ ERROR elided lifetime has a name + x +} + +fn main() {} diff --git a/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr b/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr new file mode 100644 index 0000000000000..249ae146b1675 --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr @@ -0,0 +1,40 @@ +error: elided lifetime has a name + --> $DIR/missing-lifetime-kind.rs:3:32 + | +LL | fn ampersand<'a>(x: &'a u8) -> &u8 { + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + | +note: the lint level is defined here + --> $DIR/missing-lifetime-kind.rs:1:9 + | +LL | #![deny(elided_named_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: elided lifetime has a name + --> $DIR/missing-lifetime-kind.rs:10:31 + | +LL | fn brackets<'a>(x: &'a u8) -> Brackets { + | -- ^^^^^^^^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + +error: elided lifetime has a name + --> $DIR/missing-lifetime-kind.rs:17:33 + | +LL | fn comma<'a>(x: &'a u8) -> Comma { + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + +error: elided lifetime has a name + --> $DIR/missing-lifetime-kind.rs:22:34 + | +LL | fn underscore<'a>(x: &'a u8) -> &'_ u8 { + | -- ^^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs b/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs new file mode 100644 index 0000000000000..4f9218130fbc1 --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs @@ -0,0 +1,17 @@ +#![allow(elided_named_lifetimes)] + +#[warn(elided_named_lifetimes)] +mod foo { + fn bar(x: &'static u8) -> &u8 { + //~^ WARNING elided lifetime has a name + x + } + + #[deny(elided_named_lifetimes)] + fn baz(x: &'static u8) -> &u8 { + //~^ ERROR elided lifetime has a name + x + } +} + +fn main() {} diff --git a/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr b/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr new file mode 100644 index 0000000000000..c465aab1a03d2 --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr @@ -0,0 +1,26 @@ +warning: elided lifetime has a name + --> $DIR/not-tied-to-crate.rs:5:31 + | +LL | fn bar(x: &'static u8) -> &u8 { + | ^ this elided lifetime gets resolved as `'static` + | +note: the lint level is defined here + --> $DIR/not-tied-to-crate.rs:3:8 + | +LL | #[warn(elided_named_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: elided lifetime has a name + --> $DIR/not-tied-to-crate.rs:11:31 + | +LL | fn baz(x: &'static u8) -> &u8 { + | ^ this elided lifetime gets resolved as `'static` + | +note: the lint level is defined here + --> $DIR/not-tied-to-crate.rs:10:12 + | +LL | #[deny(elided_named_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/lint/elided-named-lifetimes/static.rs b/tests/ui/lint/elided-named-lifetimes/static.rs new file mode 100644 index 0000000000000..dc8222c6e6e48 --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/static.rs @@ -0,0 +1,46 @@ +#![deny(elided_named_lifetimes)] + +use std::borrow::Cow; + +const A: &[u8] = &[]; +static B: &str = "hello"; + +trait Trait { + const C: &u8 = &0; +} + +impl Trait for () { + const C: &u8 = &1; +} + +fn ampersand(x: &'static u8) -> &u8 { + //~^ ERROR elided lifetime has a name + x +} + +struct Brackets<'a>(&'a u8); + +fn brackets(x: &'static u8) -> Brackets { + //~^ ERROR elided lifetime has a name + Brackets(x) +} + +struct Comma<'a, T>(&'a T); + +fn comma(x: &'static u8) -> Comma { + //~^ ERROR elided lifetime has a name + Comma(x) +} + +fn underscore(x: &'static u8) -> &'_ u8 { + //~^ ERROR elided lifetime has a name + x +} + +const NESTED: &Vec<&Box>> = &vec![]; + +fn main() { + const HELLO: &str = "Hello"; + static WORLD: &str = "world"; + println!("{HELLO}, {WORLD}!") +} diff --git a/tests/ui/lint/elided-named-lifetimes/static.stderr b/tests/ui/lint/elided-named-lifetimes/static.stderr new file mode 100644 index 0000000000000..d2e9776cb4f0e --- /dev/null +++ b/tests/ui/lint/elided-named-lifetimes/static.stderr @@ -0,0 +1,32 @@ +error: elided lifetime has a name + --> $DIR/static.rs:16:33 + | +LL | fn ampersand(x: &'static u8) -> &u8 { + | ^ this elided lifetime gets resolved as `'static` + | +note: the lint level is defined here + --> $DIR/static.rs:1:9 + | +LL | #![deny(elided_named_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: elided lifetime has a name + --> $DIR/static.rs:23:32 + | +LL | fn brackets(x: &'static u8) -> Brackets { + | ^^^^^^^^ this elided lifetime gets resolved as `'static` + +error: elided lifetime has a name + --> $DIR/static.rs:30:34 + | +LL | fn comma(x: &'static u8) -> Comma { + | ^ this elided lifetime gets resolved as `'static` + +error: elided lifetime has a name + --> $DIR/static.rs:35:35 + | +LL | fn underscore(x: &'static u8) -> &'_ u8 { + | ^^ this elided lifetime gets resolved as `'static` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/object-lifetime/object-lifetime-default-elision.rs b/tests/ui/object-lifetime/object-lifetime-default-elision.rs index f7c0261cfbb9c..ede6af51174b2 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-elision.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-elision.rs @@ -46,6 +46,8 @@ fn load1(ss: &dyn SomeTrait) -> &dyn SomeTrait { } fn load2<'a>(ss: &'a dyn SomeTrait) -> &dyn SomeTrait { + //~^ WARNING elided lifetime has a name + // Same as `load1` but with an explicit name thrown in for fun. ss diff --git a/tests/ui/object-lifetime/object-lifetime-default-elision.stderr b/tests/ui/object-lifetime/object-lifetime-default-elision.stderr index b59956879275a..b44a184c6848f 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-elision.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-elision.stderr @@ -1,5 +1,15 @@ +warning: elided lifetime has a name + --> $DIR/object-lifetime-default-elision.rs:48:40 + | +LL | fn load2<'a>(ss: &'a dyn SomeTrait) -> &dyn SomeTrait { + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error: lifetime may not live long enough - --> $DIR/object-lifetime-default-elision.rs:71:5 + --> $DIR/object-lifetime-default-elision.rs:73:5 | LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait { | -- -- lifetime `'b` defined here @@ -11,5 +21,5 @@ LL | ss | = help: consider adding the following bound: `'a: 'b` -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/self/elision/ignore-non-reference-lifetimes.rs b/tests/ui/self/elision/ignore-non-reference-lifetimes.rs index f7f61b8c81063..cedc6f0f9bc62 100644 --- a/tests/ui/self/elision/ignore-non-reference-lifetimes.rs +++ b/tests/ui/self/elision/ignore-non-reference-lifetimes.rs @@ -4,9 +4,11 @@ struct Foo<'a>(&'a str); impl<'b> Foo<'b> { fn a<'a>(self: Self, a: &'a str) -> &str { + //~^ WARNING elided lifetime has a name a } fn b<'a>(self: Foo<'b>, a: &'a str) -> &str { + //~^ WARNING elided lifetime has a name a } } diff --git a/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr b/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr new file mode 100644 index 0000000000000..4465dbae52989 --- /dev/null +++ b/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr @@ -0,0 +1,16 @@ +warning: elided lifetime has a name + --> $DIR/ignore-non-reference-lifetimes.rs:6:41 + | +LL | fn a<'a>(self: Self, a: &'a str) -> &str { + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + +warning: elided lifetime has a name + --> $DIR/ignore-non-reference-lifetimes.rs:10:44 + | +LL | fn b<'a>(self: Foo<'b>, a: &'a str) -> &str { + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + +warning: 2 warnings emitted + diff --git a/tests/ui/self/elision/lt-ref-self-async.fixed b/tests/ui/self/elision/lt-ref-self-async.fixed index aa1d62012da03..914511641b894 100644 --- a/tests/ui/self/elision/lt-ref-self-async.fixed +++ b/tests/ui/self/elision/lt-ref-self-async.fixed @@ -1,6 +1,6 @@ //@ edition:2018 //@ run-rustfix -#![allow(non_snake_case, dead_code)] +#![allow(non_snake_case, dead_code, elided_named_lifetimes)] use std::pin::Pin; diff --git a/tests/ui/self/elision/lt-ref-self-async.rs b/tests/ui/self/elision/lt-ref-self-async.rs index 38de0fd39f086..0c11b271c35a7 100644 --- a/tests/ui/self/elision/lt-ref-self-async.rs +++ b/tests/ui/self/elision/lt-ref-self-async.rs @@ -1,6 +1,6 @@ //@ edition:2018 //@ run-rustfix -#![allow(non_snake_case, dead_code)] +#![allow(non_snake_case, dead_code, elided_named_lifetimes)] use std::pin::Pin; diff --git a/tests/ui/self/self_lifetime-async.rs b/tests/ui/self/self_lifetime-async.rs index 7d6eb3f5eaf02..fd6902071188c 100644 --- a/tests/ui/self/self_lifetime-async.rs +++ b/tests/ui/self/self_lifetime-async.rs @@ -4,11 +4,13 @@ struct Foo<'a>(&'a ()); impl<'a> Foo<'a> { async fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } + //~^ WARNING elided lifetime has a name } type Alias = Foo<'static>; impl Alias { async fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } + //~^ WARNING elided lifetime has a name } fn main() {} diff --git a/tests/ui/self/self_lifetime-async.stderr b/tests/ui/self/self_lifetime-async.stderr new file mode 100644 index 0000000000000..32de3fd18c97f --- /dev/null +++ b/tests/ui/self/self_lifetime-async.stderr @@ -0,0 +1,18 @@ +warning: elided lifetime has a name + --> $DIR/self_lifetime-async.rs:6:44 + | +LL | async fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } + | -- ^ this elided lifetime gets resolved as `'b` + | | + | lifetime `'b` declared here + | + = note: `#[warn(elided_named_lifetimes)]` on by default + +warning: elided lifetime has a name + --> $DIR/self_lifetime-async.rs:12:52 + | +LL | async fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + +warning: 2 warnings emitted + diff --git a/tests/ui/self/self_lifetime.rs b/tests/ui/self/self_lifetime.rs index 3f655b960b163..0607c3b9317f1 100644 --- a/tests/ui/self/self_lifetime.rs +++ b/tests/ui/self/self_lifetime.rs @@ -5,11 +5,13 @@ struct Foo<'a>(&'a ()); impl<'a> Foo<'a> { fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } + //~^ WARNING elided lifetime has a name } type Alias = Foo<'static>; impl Alias { fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } + //~^ WARNING elided lifetime has a name } fn main() {} diff --git a/tests/ui/self/self_lifetime.stderr b/tests/ui/self/self_lifetime.stderr new file mode 100644 index 0000000000000..cd8f4d8adf8b1 --- /dev/null +++ b/tests/ui/self/self_lifetime.stderr @@ -0,0 +1,18 @@ +warning: elided lifetime has a name + --> $DIR/self_lifetime.rs:7:38 + | +LL | fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } + | -- ^ this elided lifetime gets resolved as `'b` + | | + | lifetime `'b` declared here + | + = note: `#[warn(elided_named_lifetimes)]` on by default + +warning: elided lifetime has a name + --> $DIR/self_lifetime.rs:13:46 + | +LL | fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + +warning: 2 warnings emitted + diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs index 735efe89cba5b..daec66709b654 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs @@ -64,6 +64,7 @@ mod in_path { // This must not err, as the `&` actually resolves to `'a`. fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) { + //~^ WARNING elided lifetime has a name f("f"); } diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 61a2925f582b2..30f4509d49dee 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -124,6 +124,14 @@ LL - fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } LL + fn g(mut x: impl Foo<()>) -> Option<()> { x.next() } | +warning: elided lifetime has a name + --> $DIR/impl-trait-missing-lifetime-gated.rs:66:57 + | +LL | fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) { + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error[E0658]: anonymous lifetimes in `impl Trait` are unstable --> $DIR/impl-trait-missing-lifetime-gated.rs:6:35 | @@ -244,7 +252,7 @@ help: consider introducing a named lifetime parameter LL | fn g<'a>(mut x: impl Foo<'a, ()>) -> Option<&()> { x.next() } | ++++ +++ -error: aborting due to 16 previous errors +error: aborting due to 16 previous errors; 1 warning emitted Some errors have detailed explanations: E0106, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs index b641f5941dcef..b61bea16e3bc8 100644 --- a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs +++ b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs @@ -100,6 +100,7 @@ where // This also works. The `'_` isn't necessary but it's where we arrive to following the suggestions: fn ok2<'a, G: 'a, T>(g: G, dest: &'a mut T) -> impl FnOnce() + '_ + 'a +//~^ WARNING elided lifetime has a name where G: Get, { diff --git a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 88a18e9d06da2..ea01dcd5020cf 100644 --- a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -6,6 +6,14 @@ LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ | | | help: consider introducing lifetime `'a` here: `'a,` +warning: elided lifetime has a name + --> $DIR/missing-lifetimes-in-signature.rs:102:64 + | +LL | fn ok2<'a, G: 'a, T>(g: G, dest: &'a mut T) -> impl FnOnce() + '_ + 'a + | -- lifetime `'a` declared here ^^ this elided lifetime gets resolved as `'a` + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error[E0700]: hidden type for `impl FnOnce()` captures lifetime that does not appear in bounds --> $DIR/missing-lifetimes-in-signature.rs:19:5 | @@ -125,7 +133,7 @@ help: consider adding an explicit lifetime bound LL | G: Get + 'a, | ++++ -error: aborting due to 8 previous errors +error: aborting due to 8 previous errors; 1 warning emitted Some errors have detailed explanations: E0261, E0309, E0311, E0621, E0700. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs index c584a58cb32e7..c178fcf5a9142 100644 --- a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs +++ b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs @@ -2,7 +2,7 @@ type Opaque2 = impl Sized; type Opaque<'a, T> = Opaque2; -fn defining<'a, T>(x: &'a i32) -> Opaque { x } +fn defining<'a, T>(x: &'a i32) -> Opaque { x } //~ WARNING elided lifetime has a name //~^ ERROR: hidden type for `Opaque2` captures lifetime that does not appear in bounds fn main() {} diff --git a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr index 03cc943d50995..e2c21f1636b0c 100644 --- a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr +++ b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr @@ -1,3 +1,13 @@ +warning: elided lifetime has a name + --> $DIR/missing_lifetime_bound.rs:5:41 + | +LL | fn defining<'a, T>(x: &'a i32) -> Opaque { x } + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + | + = note: `#[warn(elided_named_lifetimes)]` on by default + error[E0700]: hidden type for `Opaque2` captures lifetime that does not appear in bounds --> $DIR/missing_lifetime_bound.rs:5:47 | @@ -9,6 +19,6 @@ LL | fn defining<'a, T>(x: &'a i32) -> Opaque { x } | | | hidden type `&'a i32` captures the lifetime `'a` as defined here -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0700`. From d4e708f1bc92cb6e284fb8c8f9fe043fb90e226b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sat, 31 Aug 2024 21:54:29 +0200 Subject: [PATCH 11/12] Remove stray dot in `std::char::from_u32_unchecked` documentation --- library/core/src/char/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index e6574ac3c7278..fa3c2075423bc 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -122,7 +122,7 @@ pub const fn from_u32(i: u32) -> Option { self::convert::from_u32(i) } -/// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. +/// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`] /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] #[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")] From f68f66538a88a377b3510723950e97373796c5c8 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 24 Aug 2024 01:44:52 +0000 Subject: [PATCH 12/12] Create opaque definitions in resolver. --- compiler/rustc_ast/src/visit.rs | 16 ++- compiler/rustc_ast_lowering/src/lib.rs | 30 +---- .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_resolve/src/def_collector.rs | 116 ++++++++++-------- compiler/rustc_resolve/src/late.rs | 2 +- ...await.b-{closure#0}.coroutine_resume.0.mir | 8 +- .../print/coroutine-print-verbose-1.stderr | 6 +- .../ty-outlives/impl-trait-captures.stderr | 8 +- 8 files changed, 91 insertions(+), 97 deletions(-) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index fe07ec48f1f2b..7b041768983e6 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -69,14 +69,14 @@ pub enum FnKind<'a> { Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>), /// E.g., `|x, y| body`. - Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr), + Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr), } impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), - FnKind::Closure(_, _, _) => None, + FnKind::Closure(..) => None, } } @@ -90,7 +90,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, - FnKind::Closure(_, decl, _) => decl, + FnKind::Closure(_, _, decl, _) => decl, } } @@ -839,7 +839,7 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu try_visit!(walk_fn_decl(visitor, decl)); visit_opt!(visitor, visit_block, body); } - FnKind::Closure(binder, decl, body) => { + FnKind::Closure(binder, _coroutine_kind, decl, body) => { try_visit!(visitor.visit_closure_binder(binder)); try_visit!(walk_fn_decl(visitor, decl)); try_visit!(visitor.visit_expr(body)); @@ -1107,7 +1107,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V ExprKind::Closure(box Closure { binder, capture_clause, - coroutine_kind: _, + coroutine_kind, constness: _, movability: _, fn_decl, @@ -1116,7 +1116,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V fn_arg_span: _, }) => { try_visit!(visitor.visit_capture_by(capture_clause)); - try_visit!(visitor.visit_fn(FnKind::Closure(binder, fn_decl, body), *span, *id)) + try_visit!(visitor.visit_fn( + FnKind::Closure(binder, coroutine_kind, fn_decl, body), + *span, + *id + )) } ExprKind::Block(block, opt_label) => { visit_opt!(visitor, visit_label, opt_label); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bcc2c29a2ff79..cc91e55cb07e6 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -45,7 +45,6 @@ use std::collections::hash_map::Entry; use rustc_ast::node_id::NodeMap; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; -use rustc_ast_pretty::pprust; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexSet; @@ -1399,24 +1398,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span }); } - let span = t.span; - - // HACK: pprust breaks strings with newlines when the type - // gets too long. We don't want these to show up in compiler - // output or built artifacts, so replace them here... - // Perhaps we should instead format APITs more robustly. - let ident = Ident::from_str_and_span( - &pprust::ty_to_string(t).replace('\n', " "), - span, - ); - - self.create_def( - self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? - *def_node_id, - ident.name, - DefKind::TyParam, - span, - ); + let def_id = self.local_def_id(*def_node_id); + let name = self.tcx.item_name(def_id.to_def_id()); + let ident = Ident::new(name, span); let (param, bounds, path) = self.lower_universal_param_and_bounds( *def_node_id, span, @@ -1618,13 +1602,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { opaque_ty_span: Span, lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>], ) -> hir::TyKind<'hir> { - let opaque_ty_def_id = self.create_def( - self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? - opaque_ty_node_id, - kw::Empty, - DefKind::OpaqueTy, - opaque_ty_span, - ); + let opaque_ty_def_id = self.local_def_id(opaque_ty_node_id); debug!(?opaque_ty_def_id); // Map from captured (old) lifetime to synthetic (new) lifetime. diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a9b65456b8c63..ed9672a9e79b3 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1485,7 +1485,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { let disallowed = (!tilde_const_allowed).then(|| match fk { FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span }, - FnKind::Closure(_, _, _) => TildeConstReason::Closure, + FnKind::Closure(..) => TildeConstReason::Closure, }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 3595db78e9328..a70d51606f7d3 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -2,6 +2,7 @@ use std::mem; use rustc_ast::visit::FnKind; use rustc_ast::*; +use rustc_ast_pretty::pprust; use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -120,8 +121,6 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_item(&mut self, i: &'a Item) { - debug!("visit_item: {:?}", i); - // Pick the def data. This need not be unique, but the more // information we encapsulate into, the better let mut opt_macro_data = None; @@ -183,38 +182,51 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { } fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { - if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind { - match sig.header.coroutine_kind { - Some(coroutine_kind) => { - self.visit_generics(generics); - - // For async functions, we need to create their inner defs inside of a - // closure to match their desugared representation. Besides that, - // we must mirror everything that `visit::walk_fn` below does. - self.visit_fn_header(&sig.header); - for param in &sig.decl.inputs { - self.visit_param(param); - } - self.visit_fn_ret_ty(&sig.decl.output); - // If this async fn has no body (i.e. it's an async fn signature in a trait) - // then the closure_def will never be used, and we should avoid generating a - // def-id for it. - if let Some(body) = body { - let closure_def = self.create_def( - coroutine_kind.closure_id(), - kw::Empty, - DefKind::Closure, - span, - ); - self.with_parent(closure_def, |this| this.visit_block(body)); - } - return; + match fn_kind { + FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) + if let Some(coroutine_kind) = header.coroutine_kind => + { + self.visit_fn_header(header); + self.visit_generics(generics); + + // For async functions, we need to create their inner defs inside of a + // closure to match their desugared representation. Besides that, + // we must mirror everything that `visit::walk_fn` below does. + let FnDecl { inputs, output } = &**decl; + for param in inputs { + self.visit_param(param); + } + + let (return_id, return_span) = coroutine_kind.return_id(); + let return_def = + self.create_def(return_id, kw::Empty, DefKind::OpaqueTy, return_span); + self.with_parent(return_def, |this| this.visit_fn_ret_ty(output)); + + // If this async fn has no body (i.e. it's an async fn signature in a trait) + // then the closure_def will never be used, and we should avoid generating a + // def-id for it. + if let Some(body) = body { + let closure_def = self.create_def( + coroutine_kind.closure_id(), + kw::Empty, + DefKind::Closure, + span, + ); + self.with_parent(closure_def, |this| this.visit_block(body)); } - None => {} } - } + FnKind::Closure(binder, Some(coroutine_kind), decl, body) => { + self.visit_closure_binder(binder); + visit::walk_fn_decl(self, decl); - visit::walk_fn(self, fn_kind); + // Async closures desugar to closures inside of closures, so + // we must create two defs. + let coroutine_def = + self.create_def(coroutine_kind.closure_id(), kw::Empty, DefKind::Closure, span); + self.with_parent(coroutine_def, |this| visit::walk_expr(this, body)); + } + _ => visit::walk_fn(self, fn_kind), + } } fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { @@ -334,27 +346,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_expr(&mut self, expr: &'a Expr) { let parent_def = match expr.kind { ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id), - ExprKind::Closure(ref closure) => { - // Async closures desugar to closures inside of closures, so - // we must create two defs. - let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span); - match closure.coroutine_kind { - Some(coroutine_kind) => { - self.with_parent(closure_def, |this| { - let coroutine_def = this.create_def( - coroutine_kind.closure_id(), - kw::Empty, - DefKind::Closure, - expr.span, - ); - this.with_parent(coroutine_def, |this| visit::walk_expr(this, expr)); - }); - return; - } - None => closure_def, - } - } - ExprKind::Gen(_, _, _, _) => { + ExprKind::Closure(..) | ExprKind::Gen(..) => { self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) } ExprKind::ConstBlock(ref constant) => { @@ -381,6 +373,26 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { TyKind::MacCall(..) => self.visit_macro_invoc(ty.id), // Anonymous structs or unions are visited later after defined. TyKind::AnonStruct(..) | TyKind::AnonUnion(..) => {} + TyKind::ImplTrait(id, _) => { + // HACK: pprust breaks strings with newlines when the type + // gets too long. We don't want these to show up in compiler + // output or built artifacts, so replace them here... + // Perhaps we should instead format APITs more robustly. + let name = Symbol::intern(&pprust::ty_to_string(ty).replace('\n', " ")); + let kind = match self.impl_trait_context { + ImplTraitContext::Universal => DefKind::TyParam, + ImplTraitContext::Existential => DefKind::OpaqueTy, + }; + let id = self.create_def(*id, name, kind, ty.span); + match self.impl_trait_context { + // Do not nest APIT, as we desugar them as `impl_trait: bounds`, + // so the `impl_trait` node is not a parent to `bounds`. + ImplTraitContext::Universal => visit::walk_ty(self, ty), + ImplTraitContext::Existential => { + self.with_parent(id, |this| visit::walk_ty(this, ty)) + } + }; + } _ => visit::walk_ty(self, ty), } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 40fdb01a72c0e..af3d16aa4857f 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1010,7 +1010,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, this.in_func_body = previous_state; } } - FnKind::Closure(binder, declaration, body) => { + FnKind::Closure(binder, _, declaration, body) => { this.visit_closure_binder(binder); this.with_lifetime_rib( diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index c1c2fdcfa949e..109a41d1ef907 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -3,14 +3,14 @@ field_tys: { _0: CoroutineSavedTy { ty: Coroutine( - DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ (), std::future::ResumeTy, (), (), CoroutineWitness( - DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [], ), (), @@ -24,14 +24,14 @@ }, _1: CoroutineSavedTy { ty: Coroutine( - DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ (), std::future::ResumeTy, (), (), CoroutineWitness( - DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [], ), (), diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-1.stderr b/tests/ui/coroutine/print/coroutine-print-verbose-1.stderr index 934ab08cf178d..daf88fc1f2390 100644 --- a/tests/ui/coroutine/print/coroutine-print-verbose-1.stderr +++ b/tests/ui/coroutine/print/coroutine-print-verbose-1.stderr @@ -10,7 +10,7 @@ note: coroutine is not `Send` as this value is used across a yield --> $DIR/coroutine-print-verbose-1.rs:35:9 | LL | let _non_send_gen = make_non_send_coroutine(); - | ------------- has type `Opaque(DefId(0:34 ~ coroutine_print_verbose_1[75fb]::make_non_send_coroutine::{opaque#0}), [])` which is not `Send` + | ------------- has type `Opaque(DefId(0:24 ~ coroutine_print_verbose_1[75fb]::make_non_send_coroutine::{opaque#0}), [])` which is not `Send` LL | yield; | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later note: required by a bound in `require_send` @@ -33,12 +33,12 @@ note: required because it's used within this coroutine | LL | #[coroutine] || { | ^^ -note: required because it appears within the type `Opaque(DefId(0:35 ~ coroutine_print_verbose_1[75fb]::make_gen2::{opaque#0}), [Arc>])` +note: required because it appears within the type `Opaque(DefId(0:29 ~ coroutine_print_verbose_1[75fb]::make_gen2::{opaque#0}), [Arc>])` --> $DIR/coroutine-print-verbose-1.rs:41:30 | LL | pub fn make_gen2(t: T) -> impl Coroutine { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: required because it appears within the type `Opaque(DefId(0:36 ~ coroutine_print_verbose_1[75fb]::make_non_send_coroutine2::{opaque#0}), [])` +note: required because it appears within the type `Opaque(DefId(0:32 ~ coroutine_print_verbose_1[75fb]::make_non_send_coroutine2::{opaque#0}), [])` --> $DIR/coroutine-print-verbose-1.rs:47:34 | LL | fn make_non_send_coroutine2() -> impl Coroutine>> { diff --git a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr index 3ceefbc40660c..87bbd50a15c27 100644 --- a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr +++ b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -1,16 +1,16 @@ -error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), ['a/#0, T, 'a/#0])` captures lifetime that does not appear in bounds +error[E0700]: hidden type for `Opaque(DefId(0:11 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), ['a/#0, T, 'a/#0])` captures lifetime that does not appear in bounds --> $DIR/impl-trait-captures.rs:11:5 | LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { | -- ------------ opaque type defined here | | - | hidden type `&ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)) T` captures the anonymous lifetime defined here + | hidden type `&ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_)) T` captures the anonymous lifetime defined here LL | x | ^ | -help: add a `use<...>` bound to explicitly capture `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))` +help: add a `use<...>` bound to explicitly capture `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_))` | -LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + use<'a, ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)), T> { +LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + use<'a, ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_)), T> { | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error