From b7a23bc08b2cc9b1ddfac18ea2019d5150d93e0e Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Tue, 14 Nov 2023 20:36:49 +0000 Subject: On borrow return type, suggest borrowing from arg or owned return type When we encounter a function with a return type that has an anonymous lifetime with no argument to borrow from, besides suggesting the `'static` lifetime we now also suggest changing the arguments to be borrows or changing the return type to be an owned type. ``` error[E0106]: missing lifetime specifier --> $DIR/variadic-ffi-6.rs:7:6 | LL | ) -> &usize { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | ) -> &'static usize { | +++++++ help: instead, you are more likely to want to change one of the arguments to be borrowed... | LL | x: &usize, | + help: ...or alternatively, to want to return an owned value | LL - ) -> &usize { LL + ) -> usize { | ``` Fix #85843. --- .../bound-lifetime-in-binding-only.elision.stderr | 7 ++- .../bound-lifetime-in-return-only.elision.stderr | 7 ++- tests/ui/c-variadic/variadic-ffi-6.stderr | 11 ++++- tests/ui/foreign-fn-return-lifetime.fixed | 8 ---- tests/ui/foreign-fn-return-lifetime.rs | 2 - tests/ui/foreign-fn-return-lifetime.stderr | 9 +++- .../ui/generic-associated-types/issue-70304.stderr | 2 +- tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr | 2 +- tests/ui/issues/issue-13497.stderr | 6 ++- tests/ui/lifetimes/issue-26638.stderr | 16 ++++++- ...n-return-type-requires-explicit-lifetime.stderr | 50 +++++++++++++++++++--- tests/ui/self/elision/nested-item.stderr | 11 ++++- .../impl-trait-missing-lifetime-gated.stderr | 48 ++++++++++++++++++--- .../suggestions/impl-trait-missing-lifetime.stderr | 4 +- .../suggestions/missing-lifetime-specifier.stderr | 15 ++++--- tests/ui/suggestions/return-elided-lifetime.stderr | 22 ++++++++-- .../underscore-lifetime-binders.stderr | 2 +- 17 files changed, 178 insertions(+), 44 deletions(-) delete mode 100644 tests/ui/foreign-fn-return-lifetime.fixed (limited to 'tests') diff --git a/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr b/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr index 4de4afb6e92..bf22cb3b1aa 100644 --- a/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr +++ b/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr @@ -5,10 +5,15 @@ LL | fn elision &i32>() { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn elision &'static i32>() { | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn elision &i32>() { +LL + fn elision i32>() { + | error: aborting due to previous error diff --git a/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr b/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr index 7753d186504..405d5d9615b 100644 --- a/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr +++ b/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr @@ -5,10 +5,15 @@ LL | fn elision(_: fn() -> &i32) { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn elision(_: fn() -> &'static i32) { | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn elision(_: fn() -> &i32) { +LL + fn elision(_: fn() -> i32) { + | error: aborting due to previous error diff --git a/tests/ui/c-variadic/variadic-ffi-6.stderr b/tests/ui/c-variadic/variadic-ffi-6.stderr index 4c7792d9650..b3497d31a0c 100644 --- a/tests/ui/c-variadic/variadic-ffi-6.stderr +++ b/tests/ui/c-variadic/variadic-ffi-6.stderr @@ -5,10 +5,19 @@ LL | ) -> &usize { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | ) -> &'static usize { | +++++++ +help: instead, you are more likely to want to change one of the arguments to be borrowed... + | +LL | x: &usize, + | + +help: ...or alternatively, to want to return an owned value + | +LL - ) -> &usize { +LL + ) -> usize { + | error: aborting due to previous error diff --git a/tests/ui/foreign-fn-return-lifetime.fixed b/tests/ui/foreign-fn-return-lifetime.fixed deleted file mode 100644 index 143d6343d26..00000000000 --- a/tests/ui/foreign-fn-return-lifetime.fixed +++ /dev/null @@ -1,8 +0,0 @@ -// run-rustfix - -extern "C" { - pub fn g(_: &u8) -> &u8; // OK - pub fn f() -> &'static u8; //~ ERROR missing lifetime specifier -} - -fn main() {} diff --git a/tests/ui/foreign-fn-return-lifetime.rs b/tests/ui/foreign-fn-return-lifetime.rs index 76fe50a340a..35595bab36d 100644 --- a/tests/ui/foreign-fn-return-lifetime.rs +++ b/tests/ui/foreign-fn-return-lifetime.rs @@ -1,5 +1,3 @@ -// run-rustfix - extern "C" { pub fn g(_: &u8) -> &u8; // OK pub fn f() -> &u8; //~ ERROR missing lifetime specifier diff --git a/tests/ui/foreign-fn-return-lifetime.stderr b/tests/ui/foreign-fn-return-lifetime.stderr index df1a23a19ed..d63d13ec801 100644 --- a/tests/ui/foreign-fn-return-lifetime.stderr +++ b/tests/ui/foreign-fn-return-lifetime.stderr @@ -1,14 +1,19 @@ error[E0106]: missing lifetime specifier - --> $DIR/foreign-fn-return-lifetime.rs:5:19 + --> $DIR/foreign-fn-return-lifetime.rs:3:19 | LL | pub fn f() -> &u8; | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | pub fn f() -> &'static u8; | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - pub fn f() -> &u8; +LL + pub fn f() -> u8; + | error: aborting due to previous error diff --git a/tests/ui/generic-associated-types/issue-70304.stderr b/tests/ui/generic-associated-types/issue-70304.stderr index 99339e96859..9b02c1b0768 100644 --- a/tests/ui/generic-associated-types/issue-70304.stderr +++ b/tests/ui/generic-associated-types/issue-70304.stderr @@ -11,7 +11,7 @@ LL | fn create_doc() -> impl Document = DocCursorImpl<'_>> { | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | fn create_doc() -> impl Document = DocCursorImpl<'static>> { | ~~~~~~~ diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr index 443ffeb55cd..a5982a5542a 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr @@ -5,7 +5,7 @@ LL | fn d() -> impl Fn() -> (impl Debug + '_) { | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | fn d() -> impl Fn() -> (impl Debug + 'static) { | ~~~~~~~ diff --git a/tests/ui/issues/issue-13497.stderr b/tests/ui/issues/issue-13497.stderr index 4b1d979da36..5f9aa7162fa 100644 --- a/tests/ui/issues/issue-13497.stderr +++ b/tests/ui/issues/issue-13497.stderr @@ -5,10 +5,14 @@ LL | &str | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | &'static str | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL | String + | ~~~~~~ error: aborting due to previous error diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr index e61158a5d4d..bea590237bc 100644 --- a/tests/ui/lifetimes/issue-26638.stderr +++ b/tests/ui/lifetimes/issue-26638.stderr @@ -17,10 +17,18 @@ LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &'static str { iter() } | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn parse_type_2(iter: &fn(&u8)->&u8) -> &str { iter() } + | + +help: ...or alternatively, to want to return an owned value + | +LL | fn parse_type_2(iter: fn(&u8)->&u8) -> String { iter() } + | ~~~~~~ error[E0106]: missing lifetime specifier --> $DIR/issue-26638.rs:10:22 @@ -29,10 +37,14 @@ LL | fn parse_type_3() -> &str { unimplemented!() } | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn parse_type_3() -> &'static str { unimplemented!() } | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL | fn parse_type_3() -> String { unimplemented!() } + | ~~~~~~ error[E0308]: mismatched types --> $DIR/issue-26638.rs:1:69 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 5eee953ef18..caba886a3f7 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 @@ -5,10 +5,15 @@ LL | fn f() -> &isize { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn f() -> &'static isize { | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn f() -> &isize { +LL + fn f() -> isize { + | error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:7:33 @@ -41,10 +46,19 @@ LL | fn i(_x: isize) -> &isize { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn i(_x: isize) -> &'static isize { | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn i(_x: &isize) -> &isize { + | + +help: ...or alternatively, to want to return an owned value + | +LL - fn i(_x: isize) -> &isize { +LL + fn i(_x: isize) -> isize { + | error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:34:24 @@ -53,10 +67,19 @@ LL | fn j(_x: StaticStr) -> &isize { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn j(_x: StaticStr) -> &'static isize { | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn j(_x: &StaticStr) -> &isize { + | + +help: ...or alternatively, to want to return an owned value + | +LL - fn j(_x: StaticStr) -> &isize { +LL + fn j(_x: StaticStr) -> isize { + | error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:40:49 @@ -65,10 +88,19 @@ LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'a` lifetime +help: consider using the `'a` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &'a isize { | ++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn k<'a, T: WithLifetime<'a>>(_x: &T::Output) -> &isize { + | + +help: ...or alternatively, to want to return an owned value + | +LL - fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { +LL + fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> isize { + | error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:45:37 @@ -77,10 +109,18 @@ LL | fn l<'a>(_: &'a str, _: &'a str) -> &str { "" } | ------- ------- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments -help: consider using the `'a` lifetime +help: consider using the `'a` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn l<'a>(_: &'a str, _: &'a str) -> &'a str { "" } | ++ +help: instead, you are more likely to want to change one of the arguments to be borrowed... + | +LL | fn l<'a>(_: &&'a str, _: &&'a str) -> &str { "" } + | + + +help: ...or alternatively, to want to return an owned value + | +LL | fn l<'a>(_: &'a str, _: &'a str) -> String { "" } + | ~~~~~~ error: aborting due to 7 previous errors diff --git a/tests/ui/self/elision/nested-item.stderr b/tests/ui/self/elision/nested-item.stderr index 752fd82332c..5b784555910 100644 --- a/tests/ui/self/elision/nested-item.stderr +++ b/tests/ui/self/elision/nested-item.stderr @@ -21,10 +21,19 @@ LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &'static () { | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn wrap(self: &Wrap<{ fn bar(&self) {} }>) -> &() { + | + +help: ...or alternatively, to want to return an owned value + | +LL - fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() { +LL + fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> () { + | error[E0412]: cannot find type `Wrap` in this scope --> $DIR/nested-item.rs:5:15 diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 50806a67255..c4d156825a3 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -5,10 +5,19 @@ LL | fn g(mut x: impl Iterator) -> Option<&()> { x.next() } | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn g(mut x: &impl Iterator) -> Option<&()> { x.next() } + | + +help: ...or alternatively, to want to return an owned value + | +LL - fn g(mut x: impl Iterator) -> Option<&()> { x.next() } +LL + fn g(mut x: impl Iterator) -> Option<()> { x.next() } + | error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime-gated.rs:19:60 @@ -17,10 +26,19 @@ LL | async fn i(mut x: impl Iterator) -> Option<&()> { x.next() | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | async fn i(mut x: &impl Iterator) -> Option<&()> { x.next() } + | + +help: ...or alternatively, to want to return an owned value + | +LL - async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } +LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next() } + | error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime-gated.rs:27:58 @@ -29,7 +47,7 @@ LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ @@ -41,7 +59,7 @@ LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.n | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ @@ -53,10 +71,19 @@ LL | fn g(mut x: impl Foo) -> Option<&()> { x.next() } | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn g(mut x: impl Foo) -> Option<&'static ()> { x.next() } | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn g(mut x: &impl Foo) -> Option<&()> { x.next() } + | + +help: ...or alternatively, to want to return an owned value + | +LL - fn g(mut x: impl Foo) -> Option<&()> { x.next() } +LL + fn g(mut x: impl Foo) -> Option<()> { x.next() } + | error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime-gated.rs:58:41 @@ -65,10 +92,19 @@ LL | fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn g(mut x: impl Foo<()>) -> Option<&'static ()> { x.next() } | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn g(mut x: &impl Foo<()>) -> Option<&()> { x.next() } + | + +help: ...or alternatively, to want to return an owned value + | +LL - fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } +LL + fn g(mut x: impl Foo<()>) -> Option<()> { x.next() } + | error[E0658]: anonymous lifetimes in `impl Trait` are unstable --> $DIR/impl-trait-missing-lifetime-gated.rs:6:35 diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr index 2c29cfa0b91..37c2ad2aaea 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr @@ -5,7 +5,7 @@ LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ @@ -17,7 +17,7 @@ LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next( | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr index fa4bc2fa79d..484a9b3ad8d 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.stderr +++ b/tests/ui/suggestions/missing-lifetime-specifier.stderr @@ -5,7 +5,7 @@ LL | static a: RefCell>>> = RefCell::new(HashMap:: | ^^^ expected 2 lifetime parameters | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | static a: RefCell>>>> = RefCell::new(HashMap::new()); | ++++++++++++++++++ @@ -32,7 +32,7 @@ LL | static b: RefCell>>> = RefCell::new(HashMap: | expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | static b: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ ++++++++++++++++++ @@ -59,7 +59,7 @@ LL | static c: RefCell>>>> = RefCell::new(Hash | ^ expected 2 lifetime parameters | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++++++++++++ @@ -86,7 +86,7 @@ LL | static d: RefCell>>>> = RefCell::new(Has | expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ +++++++++++++++++ @@ -113,10 +113,15 @@ LL | static f: RefCell>>>> = RefCell | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - static f: RefCell>>>> = RefCell::new(HashMap::new()); +LL + static f: RefCell>>>> = RefCell::new(HashMap::new()); + | error[E0106]: missing lifetime specifier --> $DIR/missing-lifetime-specifier.rs:47:44 diff --git a/tests/ui/suggestions/return-elided-lifetime.stderr b/tests/ui/suggestions/return-elided-lifetime.stderr index 273d95bc747..54e0cfdf9a3 100644 --- a/tests/ui/suggestions/return-elided-lifetime.stderr +++ b/tests/ui/suggestions/return-elided-lifetime.stderr @@ -5,10 +5,15 @@ LL | fn f1() -> &i32 { loop {} } | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn f1() -> &'static i32 { loop {} } | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn f1() -> &i32 { loop {} } +LL + fn f1() -> i32 { loop {} } + | error[E0106]: missing lifetime specifiers --> $DIR/return-elided-lifetime.rs:8:14 @@ -19,7 +24,7 @@ LL | fn f1_() -> (&i32, &i32) { loop {} } | expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn f1_() -> (&'static i32, &'static i32) { loop {} } | +++++++ +++++++ @@ -31,10 +36,19 @@ LL | fn f2(a: i32, b: i32) -> &i32 { loop {} } | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn f2(a: i32, b: i32) -> &'static i32 { loop {} } | +++++++ +help: instead, you are more likely to want to change one of the arguments to be borrowed... + | +LL | fn f2(a: &i32, b: &i32) -> &i32 { loop {} } + | + + +help: ...or alternatively, to want to return an owned value + | +LL - fn f2(a: i32, b: i32) -> &i32 { loop {} } +LL + fn f2(a: i32, b: i32) -> i32 { loop {} } + | error[E0106]: missing lifetime specifiers --> $DIR/return-elided-lifetime.rs:13:28 @@ -45,7 +59,7 @@ LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} } | expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | LL | fn f2_(a: i32, b: i32) -> (&'static i32, &'static i32) { loop {} } | +++++++ +++++++ diff --git a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr index 50401791eff..cd74d27dcb5 100644 --- a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr +++ b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr @@ -28,7 +28,7 @@ LL | fn meh() -> Box Meh<'_>> | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | LL | fn meh() -> Box Meh<'static>> | ~~~~~~~ -- cgit 1.4.1-3-g733a5 From 5fce361d589c870d5fd2f29ff9f0cd3b6485b5be Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Tue, 14 Nov 2023 22:36:05 +0000 Subject: Account for impl Trait in lifetime suggestion When encountering ```rust fn g(mut x: impl Iterator) -> Option<&()> { /* */ } ``` Suggest ```rust fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { /* */ } ``` --- compiler/rustc_resolve/src/late/diagnostics.rs | 77 ++++++++++++++++++++-- .../impl-trait-missing-lifetime-gated.stderr | 24 +++---- 2 files changed, 84 insertions(+), 17 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a32897270a8..2998609c7c8 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2988,6 +2988,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else if let TyKind::CVarArgs = param.ty.kind { // Don't suggest `&...` for ffi fn with varargs None + } else if let TyKind::ImplTrait(..) = ¶m.ty.kind { + // We handle these in the next `else if` branch. + None } else { Some((param.ty.span.shrink_to_lo(), "&".to_string())) } @@ -3010,6 +3013,64 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { Applicability::MaybeIncorrect, ); "...or alternatively," + } else if let Some((kind, _span)) = + self.diagnostic_metadata.current_function + && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output + && !sig.decl.inputs.is_empty() + && let arg_refs = sig + .decl + .inputs + .iter() + .filter_map(|param| match ¶m.ty.kind { + TyKind::ImplTrait(_, bounds) => Some(bounds), + _ => None, + }) + .flat_map(|bounds| bounds.into_iter()) + .collect::>() + && !arg_refs.is_empty() + { + // We have a situation like + // fn g(mut x: impl Iterator) -> Option<&()> + // So we look at every ref in the trait bound. If there's any, we + // suggest + // fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> + let mut lt_finder = LifetimeFinder { + lifetime: lt.span, + found: None, + seen: vec![], + }; + for bound in arg_refs { + if let ast::GenericBound::Trait(trait_ref, _) = bound { + lt_finder.visit_trait_ref(&trait_ref.trait_ref); + } + } + lt_finder.visit_ty(ret_ty); + let spans_suggs: Vec<_> = lt_finder.seen.iter().filter_map(|ty| { + match &ty.kind { + TyKind::Ref(_, mut_ty) => { + let span = ty.span.with_hi(mut_ty.ty.span.lo()); + Some((span, "&'a ".to_string())) + } + _ => None + } + }).collect(); + self.suggest_introducing_lifetime( + err, + None, + |err, higher_ranked, span, message, intro_sugg| { + info!(?span, ?message, ?intro_sugg); + err.multipart_suggestion_verbose( + message, + std::iter::once((span, intro_sugg)) + .chain(spans_suggs.iter().cloned()) + .collect(), + Applicability::MaybeIncorrect, + ); + higher_ranked + }, + ); + "...or alternatively," } else { "instead, you are more likely" }; @@ -3019,7 +3080,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { && let FnKind::Fn(_, _, sig, _, _, _) = kind && let ast::FnRetTy::Ty(ty) = &sig.decl.output { - let mut lt_finder = LifetimeFinder { lifetime: lt.span, found: None }; + let mut lt_finder = LifetimeFinder { + lifetime: lt.span, + found: None, + seen: vec![], + }; lt_finder.visit_ty(&ty); if let Some(ty) = lt_finder.found { @@ -3155,14 +3220,16 @@ pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: I struct LifetimeFinder<'ast> { lifetime: Span, found: Option<&'ast Ty>, + seen: Vec<&'ast Ty>, } impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> { fn visit_ty(&mut self, t: &'ast Ty) { - if t.span.lo() == self.lifetime.lo() - && let TyKind::Ref(_, mut_ty) = &t.kind - { - self.found = Some(&mut_ty.ty); + if let TyKind::Ref(_, mut_ty) = &t.kind { + self.seen.push(t); + if t.span.lo() == self.lifetime.lo() { + self.found = Some(&mut_ty.ty); + } } walk_ty(self, t) } diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index c4d156825a3..3425092366f 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -9,10 +9,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | +++++++ -help: instead, you are more likely to want to change the argument to be borrowed... +help: consider introducing a named lifetime parameter | -LL | fn g(mut x: &impl Iterator) -> Option<&()> { x.next() } - | + +LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | ++++ ~~~ ~~~ help: ...or alternatively, to want to return an owned value | LL - fn g(mut x: impl Iterator) -> Option<&()> { x.next() } @@ -30,10 +30,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | +++++++ -help: instead, you are more likely to want to change the argument to be borrowed... +help: consider introducing a named lifetime parameter | -LL | async fn i(mut x: &impl Iterator) -> Option<&()> { x.next() } - | + +LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | ++++ ~~~ ~~~ help: ...or alternatively, to want to return an owned value | LL - async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } @@ -75,10 +75,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | fn g(mut x: impl Foo) -> Option<&'static ()> { x.next() } | +++++++ -help: instead, you are more likely to want to change the argument to be borrowed... +help: consider introducing a named lifetime parameter | -LL | fn g(mut x: &impl Foo) -> Option<&()> { x.next() } - | + +LL | fn g<'a>(mut x: impl Foo) -> Option<&'a ()> { x.next() } + | ++++ ~~~ help: ...or alternatively, to want to return an owned value | LL - fn g(mut x: impl Foo) -> Option<&()> { x.next() } @@ -96,10 +96,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | fn g(mut x: impl Foo<()>) -> Option<&'static ()> { x.next() } | +++++++ -help: instead, you are more likely to want to change the argument to be borrowed... +help: consider introducing a named lifetime parameter | -LL | fn g(mut x: &impl Foo<()>) -> Option<&()> { x.next() } - | + +LL | fn g<'a>(mut x: impl Foo<()>) -> Option<&'a ()> { x.next() } + | ++++ ~~~ help: ...or alternatively, to want to return an owned value | LL - fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } -- cgit 1.4.1-3-g733a5 From d30252e3593af0c335f86de4242d581ffa11e270 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Tue, 14 Nov 2023 22:41:45 +0000 Subject: Tweak wording --- compiler/rustc_resolve/src/late/diagnostics.rs | 8 ++++---- tests/ui/c-variadic/variadic-ffi-6.stderr | 2 +- tests/ui/lifetimes/issue-26638.stderr | 2 +- ...lifetime-elision-return-type-requires-explicit-lifetime.stderr | 8 ++++---- tests/ui/self/elision/nested-item.stderr | 2 +- tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr | 8 ++++---- tests/ui/suggestions/return-elided-lifetime.stderr | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2998609c7c8..2cd3e673382 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3012,7 +3012,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { sugg, Applicability::MaybeIncorrect, ); - "...or alternatively," + "...or alternatively, you might want" } else if let Some((kind, _span)) = self.diagnostic_metadata.current_function && let FnKind::Fn(_, _, sig, _, _, _) = kind @@ -3070,9 +3070,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { higher_ranked }, ); - "...or alternatively," + "alternatively, you might want" } else { - "instead, you are more likely" + "instead, you are more likely to want" }; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = @@ -3105,7 +3105,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } }; err.multipart_suggestion_verbose( - format!("{pre} to want to return an owned value"), + format!("{pre} to return an owned value"), sugg, Applicability::MaybeIncorrect, ); diff --git a/tests/ui/c-variadic/variadic-ffi-6.stderr b/tests/ui/c-variadic/variadic-ffi-6.stderr index b3497d31a0c..3511127a9fa 100644 --- a/tests/ui/c-variadic/variadic-ffi-6.stderr +++ b/tests/ui/c-variadic/variadic-ffi-6.stderr @@ -13,7 +13,7 @@ help: instead, you are more likely to want to change one of the arguments to be | LL | x: &usize, | + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL - ) -> &usize { LL + ) -> usize { diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr index bea590237bc..ee958686259 100644 --- a/tests/ui/lifetimes/issue-26638.stderr +++ b/tests/ui/lifetimes/issue-26638.stderr @@ -25,7 +25,7 @@ help: instead, you are more likely to want to change the argument to be borrowed | LL | fn parse_type_2(iter: &fn(&u8)->&u8) -> &str { iter() } | + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL | fn parse_type_2(iter: fn(&u8)->&u8) -> String { iter() } | ~~~~~~ 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 caba886a3f7..37ebb178ec6 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 @@ -54,7 +54,7 @@ help: instead, you are more likely to want to change the argument to be borrowed | LL | fn i(_x: &isize) -> &isize { | + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL - fn i(_x: isize) -> &isize { LL + fn i(_x: isize) -> isize { @@ -75,7 +75,7 @@ help: instead, you are more likely to want to change the argument to be borrowed | LL | fn j(_x: &StaticStr) -> &isize { | + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL - fn j(_x: StaticStr) -> &isize { LL + fn j(_x: StaticStr) -> isize { @@ -96,7 +96,7 @@ help: instead, you are more likely to want to change the argument to be borrowed | LL | fn k<'a, T: WithLifetime<'a>>(_x: &T::Output) -> &isize { | + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL - fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { LL + fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> isize { @@ -117,7 +117,7 @@ help: instead, you are more likely to want to change one of the arguments to be | LL | fn l<'a>(_: &&'a str, _: &&'a str) -> &str { "" } | + + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL | fn l<'a>(_: &'a str, _: &'a str) -> String { "" } | ~~~~~~ diff --git a/tests/ui/self/elision/nested-item.stderr b/tests/ui/self/elision/nested-item.stderr index 5b784555910..7bad26fa133 100644 --- a/tests/ui/self/elision/nested-item.stderr +++ b/tests/ui/self/elision/nested-item.stderr @@ -29,7 +29,7 @@ help: instead, you are more likely to want to change the argument to be borrowed | LL | fn wrap(self: &Wrap<{ fn bar(&self) {} }>) -> &() { | + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL - fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() { LL + fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> () { diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 3425092366f..a1ab4392214 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -13,7 +13,7 @@ help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } | ++++ ~~~ ~~~ -help: ...or alternatively, to want to return an owned value +help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Iterator) -> Option<&()> { x.next() } LL + fn g(mut x: impl Iterator) -> Option<()> { x.next() } @@ -34,7 +34,7 @@ help: consider introducing a named lifetime parameter | LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } | ++++ ~~~ ~~~ -help: ...or alternatively, to want to return an owned value +help: alternatively, you might want to return an owned value | LL - async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next() } @@ -79,7 +79,7 @@ help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Foo) -> Option<&'a ()> { x.next() } | ++++ ~~~ -help: ...or alternatively, to want to return an owned value +help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Foo) -> Option<&()> { x.next() } LL + fn g(mut x: impl Foo) -> Option<()> { x.next() } @@ -100,7 +100,7 @@ help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Foo<()>) -> Option<&'a ()> { x.next() } | ++++ ~~~ -help: ...or alternatively, to want to return an owned value +help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } LL + fn g(mut x: impl Foo<()>) -> Option<()> { x.next() } diff --git a/tests/ui/suggestions/return-elided-lifetime.stderr b/tests/ui/suggestions/return-elided-lifetime.stderr index 54e0cfdf9a3..7bfffd30184 100644 --- a/tests/ui/suggestions/return-elided-lifetime.stderr +++ b/tests/ui/suggestions/return-elided-lifetime.stderr @@ -44,7 +44,7 @@ help: instead, you are more likely to want to change one of the arguments to be | LL | fn f2(a: &i32, b: &i32) -> &i32 { loop {} } | + + -help: ...or alternatively, to want to return an owned value +help: ...or alternatively, you might want to return an owned value | LL - fn f2(a: i32, b: i32) -> &i32 { loop {} } LL + fn f2(a: i32, b: i32) -> i32 { loop {} } -- cgit 1.4.1-3-g733a5 From dec7f00e158f04054320f63796fea1445ac18917 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 15 Nov 2023 04:07:19 +0000 Subject: Fix incorrect lifetime suggestion --- compiler/rustc_resolve/src/late/diagnostics.rs | 4 ++-- ...on-return-type-requires-explicit-lifetime.stderr | 21 ++------------------- 2 files changed, 4 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2cd3e673382..ce981c333d8 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2853,8 +2853,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { "this function's return type contains a borrowed value, but there is no value \ for it to be borrowed from", ); - maybe_static = true; if in_scope_lifetimes.is_empty() { + maybe_static = true; in_scope_lifetimes = vec![( Ident::with_dummy_span(kw::StaticLifetime), (DUMMY_NODE_ID, LifetimeRes::Static), @@ -2865,8 +2865,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { "this function's return type contains a borrowed value with an elided \ lifetime, but the lifetime cannot be derived from the arguments", ); - maybe_static = true; if in_scope_lifetimes.is_empty() { + maybe_static = true; in_scope_lifetimes = vec![( Ident::with_dummy_span(kw::StaticLifetime), (DUMMY_NODE_ID, LifetimeRes::Static), 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 37ebb178ec6..23ef36888f0 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 @@ -88,19 +88,10 @@ LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'a` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` +help: consider using the `'a` lifetime | LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &'a isize { | ++ -help: instead, you are more likely to want to change the argument to be borrowed... - | -LL | fn k<'a, T: WithLifetime<'a>>(_x: &T::Output) -> &isize { - | + -help: ...or alternatively, you might want to return an owned value - | -LL - fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { -LL + fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> isize { - | error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:45:37 @@ -109,18 +100,10 @@ LL | fn l<'a>(_: &'a str, _: &'a str) -> &str { "" } | ------- ------- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments -help: consider using the `'a` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` +help: consider using the `'a` lifetime | LL | fn l<'a>(_: &'a str, _: &'a str) -> &'a str { "" } | ++ -help: instead, you are more likely to want to change one of the arguments to be borrowed... - | -LL | fn l<'a>(_: &&'a str, _: &&'a str) -> &str { "" } - | + + -help: ...or alternatively, you might want to return an owned value - | -LL | fn l<'a>(_: &'a str, _: &'a str) -> String { "" } - | ~~~~~~ error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 2a92d820c79e56be8191bdf64e8b2dc7c96210df Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 15 Nov 2023 16:56:41 +0000 Subject: Suggest 'a when trait object assoc type has '_ --- compiler/rustc_resolve/src/late/diagnostics.rs | 33 ++++++++++++++-------- .../impl-trait-missing-lifetime-gated.stderr | 8 ++++++ .../suggestions/impl-trait-missing-lifetime.stderr | 8 ++++++ 3 files changed, 37 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index ce981c333d8..984516baa18 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2971,9 +2971,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { // we identified that the return expression references only one argument, we // would suggest borrowing only that argument, and we'd skip the prior // "use `'static`" suggestion entirely. - if let [lt] = &lifetime_refs[..] && lt.kind == MissingLifetimeKind::Ampersand { - let pre = if let Some((kind, _span)) = - self.diagnostic_metadata.current_function + if let [lt] = &lifetime_refs[..] + && (lt.kind == MissingLifetimeKind::Ampersand + || lt.kind == MissingLifetimeKind::Underscore) + { + let pre = if lt.kind == MissingLifetimeKind::Ampersand + && let Some((kind, _span)) = + self.diagnostic_metadata.current_function && let FnKind::Fn(_, _, sig, _, _, _) = kind && !sig.decl.inputs.is_empty() && let sugg = sig @@ -3013,8 +3017,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { Applicability::MaybeIncorrect, ); "...or alternatively, you might want" - } else if let Some((kind, _span)) = - self.diagnostic_metadata.current_function + } else if (lt.kind == MissingLifetimeKind::Ampersand + || lt.kind == MissingLifetimeKind::Underscore) + && let Some((kind, _span)) = + self.diagnostic_metadata.current_function && let FnKind::Fn(_, _, sig, _, _, _) = kind && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output && !sig.decl.inputs.is_empty() @@ -3059,7 +3065,6 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { err, None, |err, higher_ranked, span, message, intro_sugg| { - info!(?span, ?message, ?intro_sugg); err.multipart_suggestion_verbose( message, std::iter::once((span, intro_sugg)) @@ -3093,7 +3098,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { && segments[0].ident.name == sym::str { // Don't suggest `-> str`, suggest `-> String`. - sugg = vec![(lt.span.with_hi(ty.span.hi()), "String".to_string())]; + sugg = vec![ + (lt.span.with_hi(ty.span.hi()), "String".to_string()), + ]; } if let TyKind::Slice(inner_ty) = &ty.kind { // Don't suggest `-> [T]`, suggest `-> Vec`. @@ -3104,11 +3111,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } }; - err.multipart_suggestion_verbose( - format!("{pre} to return an owned value"), - sugg, - Applicability::MaybeIncorrect, - ); + if lt.kind == MissingLifetimeKind::Ampersand { + err.multipart_suggestion_verbose( + format!("{pre} to return an owned value"), + sugg, + Applicability::MaybeIncorrect, + ); + } } } diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index a1ab4392214..383cba75e28 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -51,6 +51,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ +help: consider introducing a named lifetime parameter + | +LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | ++++ ~~~ ~~~ error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime-gated.rs:37:64 @@ -63,6 +67,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ +help: consider introducing a named lifetime parameter + | +LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | ++++ ~~~ ~~~ error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime-gated.rs:47:37 diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr index 37c2ad2aaea..98b0ab82b3c 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr @@ -9,6 +9,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ +help: consider introducing a named lifetime parameter + | +LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | ++++ ~~~ ~~~ error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime.rs:16:60 @@ -21,6 +25,10 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } | ~~~~~~~ +help: consider introducing a named lifetime parameter + | +LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | ++++ ~~~ ~~~ error: lifetime may not live long enough --> $DIR/impl-trait-missing-lifetime.rs:16:69 -- cgit 1.4.1-3-g733a5 From 85f26ade8dc3364f29a2c0b0d8f5fe2068df8969 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 15 Nov 2023 18:00:43 +0000 Subject: Account for '_ in lifetime suggestion --- compiler/rustc_resolve/src/late/diagnostics.rs | 19 +++++++++++++++---- .../impl-trait-missing-lifetime-gated.stderr | 10 ++++++++++ .../ui/suggestions/impl-trait-missing-lifetime.stderr | 10 ++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 984516baa18..f7560d9949b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3079,6 +3079,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else { "instead, you are more likely to want" }; + let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = self.diagnostic_metadata.current_function @@ -3092,6 +3093,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }; lt_finder.visit_ty(&ty); + if let [Ty { span, kind: TyKind::Ref(_, mut_ty), ..}] + = <_finder.seen[..] + { + // We might have a situation like + // fn g(mut x: impl Iterator) -> Option<&'_ ()> + // but `lt.span` only points at `'_`, so to suggest `-> Option<()>` + // we need to find a more accurate span to end up with + // fn g<'a>(mut x: impl Iterator) -> Option<()> + sugg = vec![(span.with_hi(mut_ty.ty.span.lo()), String::new())]; + owned_sugg = true; + } if let Some(ty) = lt_finder.found { if let TyKind::Path(None, Path { segments, .. }) = &ty.kind && segments.len() == 1 @@ -3101,8 +3113,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { sugg = vec![ (lt.span.with_hi(ty.span.hi()), "String".to_string()), ]; - } - if let TyKind::Slice(inner_ty) = &ty.kind { + } else if let TyKind::Slice(inner_ty) = &ty.kind { // Don't suggest `-> [T]`, suggest `-> Vec`. sugg = vec![ (lt.span.with_hi(inner_ty.span.lo()), "Vec<".to_string()), @@ -3110,8 +3121,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ]; } } - }; - if lt.kind == MissingLifetimeKind::Ampersand { + } + if owned_sugg { err.multipart_suggestion_verbose( format!("{pre} to return an owned value"), sugg, diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 383cba75e28..2dfaa731194 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -55,6 +55,11 @@ help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } | ++++ ~~~ ~~~ +help: alternatively, you might want to return an owned value + | +LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + fn g(mut x: impl Iterator) -> Option<()> { x.next() } + | error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime-gated.rs:37:64 @@ -71,6 +76,11 @@ help: consider introducing a named lifetime parameter | LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } | ++++ ~~~ ~~~ +help: alternatively, you might want to return an owned value + | +LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next() } + | error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime-gated.rs:47:37 diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr index 98b0ab82b3c..c1dbaae0649 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr @@ -13,6 +13,11 @@ help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } | ++++ ~~~ ~~~ +help: alternatively, you might want to return an owned value + | +LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + fn g(mut x: impl Iterator) -> Option<()> { x.next() } + | error[E0106]: missing lifetime specifier --> $DIR/impl-trait-missing-lifetime.rs:16:60 @@ -29,6 +34,11 @@ help: consider introducing a named lifetime parameter | LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } | ++++ ~~~ ~~~ +help: alternatively, you might want to return an owned value + | +LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next() } + | error: lifetime may not live long enough --> $DIR/impl-trait-missing-lifetime.rs:16:69 -- cgit 1.4.1-3-g733a5 From bb9d720a161221dac6ad7f0fb1cb5e94ab157ab2 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 15 Nov 2023 20:09:19 +0000 Subject: Do not consider traits as ownable in suggestion --- compiler/rustc_resolve/src/late/diagnostics.rs | 62 +++++++++++++++++++--- .../suggestions/missing-lifetime-specifier.stderr | 5 -- 2 files changed, 55 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f7560d9949b..86846aa214a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3105,15 +3105,63 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { owned_sugg = true; } if let Some(ty) = lt_finder.found { - if let TyKind::Path(None, Path { segments, .. }) = &ty.kind + if let TyKind::Path(None, path @ Path { segments, .. }) = &ty.kind && segments.len() == 1 - && segments[0].ident.name == sym::str { - // Don't suggest `-> str`, suggest `-> String`. - sugg = vec![ - (lt.span.with_hi(ty.span.hi()), "String".to_string()), - ]; - } else if let TyKind::Slice(inner_ty) = &ty.kind { + if segments[0].ident.name == sym::str { + // Don't suggest `-> str`, suggest `-> String`. + sugg = vec![ + (lt.span.with_hi(ty.span.hi()), "String".to_string()), + ]; + } else { + // Check if the path being borrowed is likely to be owned. + let path: Vec<_> = Segment::from_path(path); + match self.resolve_path(&path, Some(TypeNS), None) { + PathResult::Module( + ModuleOrUniformRoot::Module(module), + ) => { + match module.res() { + Some(Res::PrimTy(..)) => {} + Some(Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::ForeignTy + | DefKind::AssocTy + | DefKind::OpaqueTy + | DefKind::TyParam, + _, + )) => {} + _ => { // Do not suggest in all other cases. + owned_sugg = false; + } + } + } + PathResult::NonModule(res) => { + match res.base_res() { + Res::PrimTy(..) => {} + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::ForeignTy + | DefKind::AssocTy + | DefKind::OpaqueTy + | DefKind::TyParam, + _, + ) => {} + _ => { // Do not suggest in all other cases. + owned_sugg = false; + } + } + } + _ => { // Do not suggest in all other cases. + owned_sugg = false; + } + } + } + } + if let TyKind::Slice(inner_ty) = &ty.kind { // Don't suggest `-> [T]`, suggest `-> Vec`. sugg = vec![ (lt.span.with_hi(inner_ty.span.lo()), "Vec<".to_string()), diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr index 484a9b3ad8d..e41f547ce9b 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.stderr +++ b/tests/ui/suggestions/missing-lifetime-specifier.stderr @@ -117,11 +117,6 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ -help: instead, you are more likely to want to return an owned value - | -LL - static f: RefCell>>>> = RefCell::new(HashMap::new()); -LL + static f: RefCell>>>> = RefCell::new(HashMap::new()); - | error[E0106]: missing lifetime specifier --> $DIR/missing-lifetime-specifier.rs:47:44 -- cgit 1.4.1-3-g733a5 From 69a2bd6c044ac4d638f901ff2f5849c6eee3b08e Mon Sep 17 00:00:00 2001 From: Lenko Donchev Date: Sat, 14 Oct 2023 14:47:24 -0500 Subject: report_not_const_evaluatable_error to avoid ICEing on ConstKind::Expr --- .../traits/error_reporting/type_err_ctxt_ext.rs | 36 +++++++----- .../const_kind_expr/issue_114151.rs | 25 +++++++++ .../const_kind_expr/issue_114151.stderr | 64 ++++++++++++++++++++++ 3 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr (limited to 'tests') diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 8fa0dceda87..4988222c135 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -3525,20 +3525,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + let mut err = + self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); + let const_span = self.tcx.def_span(uv.def); + match self.tcx.sess.source_map().span_to_snippet(const_span) { + Ok(snippet) => err.help(format!( + "try adding a `where` bound using this expression: `where [(); {snippet}]:`" + )), + _ => err.help("consider adding a `where` bound using this expression"), + }; + Some(err) + } + ty::ConstKind::Expr(_) => { + let err = self + .tcx + .sess + .struct_span_err(span, format!("unconstrained generic constant `{ct}`")); + Some(err) + } + _ => { bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); - }; - let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); - let const_span = self.tcx.def_span(uv.def); - match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(format!( - "try adding a `where` bound using this expression: `where [(); {snippet}]:`" - )), - _ => err.help("consider adding a `where` bound using this expression"), - }; - Some(err) - } + } + }, _ => { span_bug!( span, diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs new file mode 100644 index 00000000000..6256000b491 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs @@ -0,0 +1,25 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +fn foo( + _: [u8; { + { + N + } + }], +) { +} + +fn ice() +where + [(); (L - 1) + 1 + L]:, +{ + foo::<_, L>([(); L + 1 + L]); + //~^ ERROR: mismatched types + //~^^ ERROR: unconstrained generic constant + //~^^^ ERROR: function takes 1 generic argument but 2 generic arguments were supplied + //~^^^^ ERROR: unconstrained generic constant + //~^^^^^ ERROR: unconstrained generic constant `{const expr}` +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr new file mode 100644 index 00000000000..6001d824787 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr @@ -0,0 +1,64 @@ +error[E0107]: function takes 1 generic argument but 2 generic arguments were supplied + --> $DIR/issue_114151.rs:17:5 + | +LL | foo::<_, L>([(); L + 1 + L]); + | ^^^ - help: remove this generic argument + | | + | expected 1 generic argument + | +note: function defined here, with 1 generic parameter: `N` + --> $DIR/issue_114151.rs:4:4 + | +LL | fn foo( + | ^^^ -------------- + +error[E0308]: mismatched types + --> $DIR/issue_114151.rs:17:18 + | +LL | foo::<_, L>([(); L + 1 + L]); + | ^^ expected `u8`, found `()` + +error: unconstrained generic constant + --> $DIR/issue_114151.rs:17:22 + | +LL | foo::<_, L>([(); L + 1 + L]); + | ^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:` + +error: unconstrained generic constant + --> $DIR/issue_114151.rs:17:17 + | +LL | foo::<_, L>([(); L + 1 + L]); + | ----------- ^^^^^^^^^^^^^^^ + | | + | required by a bound introduced by this call + | + = help: try adding a `where` bound using this expression: `where [(); { + { + N + } + }]:` +note: required by a bound in `foo` + --> $DIR/issue_114151.rs:5:13 + | +LL | fn foo( + | --- required by a bound in this function +LL | _: [u8; { + | _____________^ +LL | | { +LL | | N +LL | | } +LL | | }], + | |_____^ required by this bound in `foo` + +error: unconstrained generic constant `{const expr}` + --> $DIR/issue_114151.rs:17:5 + | +LL | foo::<_, L>([(); L + 1 + L]); + | ^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0107, E0308. +For more information about an error, try `rustc --explain E0107`. -- cgit 1.4.1-3-g733a5 From 226edf64fa58e591d8abc6afef5e38c15c181698 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 12 Dec 2023 09:55:50 +1100 Subject: Improve an error involving attribute values. Attribute values must be literals. The error you get when that doesn't hold is pretty bad, e.g.: ``` unexpected expression: 1 + 1 ``` You also get the same error if the attribute value is a literal, but an invalid literal, e.g.: ``` unexpected expression: "foo"suffix ``` This commit does two things. - Changes the error message to "attribute value must be a literal", which gives a better idea of what the problem is and how to fix it. It also no longer prints the invalid expression, because the carets below highlight it anyway. - Separates the "not a literal" case from the "invalid literal" case. Which means invalid literals now get the specific error at the literal level, rather than at the attribute level. --- compiler/rustc_parse/src/validate_attr.rs | 60 ++++++++++++++-------- tests/ui/attributes/issue-90873.rs | 4 +- tests/ui/attributes/issue-90873.stderr | 9 +--- tests/ui/attributes/key-value-expansion-on-mac.rs | 4 +- .../attributes/key-value-expansion-on-mac.stderr | 2 +- tests/ui/attributes/key-value-expansion.rs | 6 +-- tests/ui/attributes/key-value-expansion.stderr | 6 +-- tests/ui/attributes/unused-item-in-attr.rs | 2 +- tests/ui/attributes/unused-item-in-attr.stderr | 4 +- tests/ui/consts/issue-90878-2.rs | 2 +- tests/ui/consts/issue-90878-2.stderr | 2 +- tests/ui/malformed/malformed-interpolated.rs | 4 +- tests/ui/malformed/malformed-interpolated.stderr | 4 +- tests/ui/parser/bad-lit-suffixes.rs | 5 +- tests/ui/parser/bad-lit-suffixes.stderr | 27 +++++++--- tests/ui/parser/issues/issue-104620.rs | 2 +- tests/ui/parser/issues/issue-104620.stderr | 6 ++- 17 files changed, 87 insertions(+), 62 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index cbe75b3dab6..81055431f64 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -6,9 +6,9 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::MetaItemKind; use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem}; -use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use rustc_session::errors::report_lit_error; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, Symbol}; @@ -51,28 +51,44 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta MetaItemKind::List(nmis) } AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => { - if let ast::ExprKind::Lit(token_lit) = expr.kind - && let Ok(lit) = ast::MetaItemLit::from_token_lit(token_lit, expr.span) - { - if token_lit.suffix.is_some() { - let mut err = sess.span_diagnostic.struct_span_err( - expr.span, - "suffixed literals are not allowed in attributes", - ); - err.help( - "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ - use an unsuffixed version (`1`, `1.0`, etc.)", - ); - return Err(err); - } else { - MetaItemKind::NameValue(lit) - } + if let ast::ExprKind::Lit(token_lit) = expr.kind { + let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span); + let res = match res { + Ok(lit) => { + if token_lit.suffix.is_some() { + let mut err = sess.span_diagnostic.struct_span_err( + expr.span, + "suffixed literals are not allowed in attributes", + ); + err.help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ + use an unsuffixed version (`1`, `1.0`, etc.)", + ); + return Err(err); + } else { + MetaItemKind::NameValue(lit) + } + } + Err(err) => { + report_lit_error(sess, err, token_lit, expr.span); + let lit = ast::MetaItemLit { + symbol: token_lit.symbol, + suffix: token_lit.suffix, + kind: ast::LitKind::Err, + span: expr.span, + }; + MetaItemKind::NameValue(lit) + } + }; + res } else { - // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can - // happen with e.g. `#[foo = include_str!("nonexistent-file.rs")]`; in that - // case we delay the error because an earlier error will have already been - // reported. - let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr)); + // Example cases: + // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`. + // - `#[foo = include_str!("nonexistent-file.rs")]`: + // results in `ast::ExprKind::Err`. In that case we delay + // the error because an earlier error will have already + // been reported. + let msg = format!("attribute value must be a literal"); let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg); if let ast::ExprKind::Err = expr.kind { err.downgrade_to_delayed_bug(); diff --git a/tests/ui/attributes/issue-90873.rs b/tests/ui/attributes/issue-90873.rs index 1411f61744d..53339ce7e28 100644 --- a/tests/ui/attributes/issue-90873.rs +++ b/tests/ui/attributes/issue-90873.rs @@ -1,9 +1,9 @@ #![u=||{static d=||1;}] -//~^ unexpected expression +//~^ attribute value must be a literal //~| cannot find attribute `u` in this scope //~| missing type for `static` item #![a={impl std::ops::Neg for i8 {}}] -//~^ ERROR unexpected expression +//~^ ERROR attribute value must be a literal //~| ERROR cannot find attribute `a` in this scope //~| ERROR `main` function not found in crate `issue_90873` diff --git a/tests/ui/attributes/issue-90873.stderr b/tests/ui/attributes/issue-90873.stderr index 894ec8341f8..5a8bbaf8ec1 100644 --- a/tests/ui/attributes/issue-90873.stderr +++ b/tests/ui/attributes/issue-90873.stderr @@ -1,15 +1,10 @@ -error: unexpected expression: `|| - { - static d: _ = || 1; - }` +error: attribute value must be a literal --> $DIR/issue-90873.rs:1:6 | LL | #![u=||{static d=||1;}] | ^^^^^^^^^^^^^^^^^ -error: unexpected expression: `{ - impl std::ops::Neg for i8 {} - }` +error: attribute value must be a literal --> $DIR/issue-90873.rs:6:6 | LL | #![a={impl std::ops::Neg for i8 {}}] diff --git a/tests/ui/attributes/key-value-expansion-on-mac.rs b/tests/ui/attributes/key-value-expansion-on-mac.rs index c1d68d8cda9..ea7cf7c4f64 100644 --- a/tests/ui/attributes/key-value-expansion-on-mac.rs +++ b/tests/ui/attributes/key-value-expansion-on-mac.rs @@ -7,8 +7,8 @@ macro_rules! bar { // FIXME?: `bar` here expands before `stringify` has a chance to expand. // `#[rustc_dummy = ...]` is validated and dropped during expansion of `bar`, -// the "unexpected expression" errors comes from the validation. -#[rustc_dummy = stringify!(b)] //~ ERROR unexpected expression: `stringify!(b)` +// the "attribute value must be a literal" error comes from the validation. +#[rustc_dummy = stringify!(b)] //~ ERROR attribute value must be a literal bar!(); fn main() {} diff --git a/tests/ui/attributes/key-value-expansion-on-mac.stderr b/tests/ui/attributes/key-value-expansion-on-mac.stderr index 7d817da1362..260462cfeef 100644 --- a/tests/ui/attributes/key-value-expansion-on-mac.stderr +++ b/tests/ui/attributes/key-value-expansion-on-mac.stderr @@ -1,4 +1,4 @@ -error: unexpected expression: `stringify!(b)` +error: attribute value must be a literal --> $DIR/key-value-expansion-on-mac.rs:11:17 | LL | #[rustc_dummy = stringify!(b)] diff --git a/tests/ui/attributes/key-value-expansion.rs b/tests/ui/attributes/key-value-expansion.rs index 83d601e5e3a..3065c12749c 100644 --- a/tests/ui/attributes/key-value-expansion.rs +++ b/tests/ui/attributes/key-value-expansion.rs @@ -18,13 +18,13 @@ macro_rules! bug { // Any expressions containing macro call `X` that's more complex than `X` itself. // Parentheses will work. -bug!((column!())); //~ ERROR unexpected expression: `(7u32)` +bug!((column!())); //~ ERROR attribute value must be a literal // Original test case. macro_rules! bug { () => { - bug!("bug" + stringify!(found)); //~ ERROR unexpected expression: `"bug" + "found"` + bug!("bug" + stringify!(found)); //~ ERROR attribute value must be a literal }; ($test:expr) => { #[doc = $test] @@ -46,7 +46,7 @@ macro_rules! doc_comment { macro_rules! some_macro { ($t1: ty) => { doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()} - //~^ ERROR unexpected expression: `{ + //~^ ERROR attribute value must be a literal }; } diff --git a/tests/ui/attributes/key-value-expansion.stderr b/tests/ui/attributes/key-value-expansion.stderr index aaa8b169583..54d79c5bebb 100644 --- a/tests/ui/attributes/key-value-expansion.stderr +++ b/tests/ui/attributes/key-value-expansion.stderr @@ -1,10 +1,10 @@ -error: unexpected expression: `(7u32)` +error: attribute value must be a literal --> $DIR/key-value-expansion.rs:21:6 | LL | bug!((column!())); | ^^^^^^^^^^^ -error: unexpected expression: `"bug" + "found"` +error: attribute value must be a literal --> $DIR/key-value-expansion.rs:27:14 | LL | bug!("bug" + stringify!(found)); @@ -15,7 +15,7 @@ LL | bug!(); | = note: this error originates in the macro `bug` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unexpected expression: `{ let res = ::alloc::fmt::format(format_args!("{0}", "u8")); res }.as_str()` +error: attribute value must be a literal --> $DIR/key-value-expansion.rs:48:23 | LL | doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()} diff --git a/tests/ui/attributes/unused-item-in-attr.rs b/tests/ui/attributes/unused-item-in-attr.rs index 70dcd5413f1..fda0a5d6a3f 100644 --- a/tests/ui/attributes/unused-item-in-attr.rs +++ b/tests/ui/attributes/unused-item-in-attr.rs @@ -1,5 +1,5 @@ #[w = { extern crate alloc; }] -//~^ ERROR unexpected expression: `{ +//~^ ERROR attribute value must be a literal //~| ERROR cannot find attribute `w` in this scope fn f() {} diff --git a/tests/ui/attributes/unused-item-in-attr.stderr b/tests/ui/attributes/unused-item-in-attr.stderr index 92a8f585821..84130965d31 100644 --- a/tests/ui/attributes/unused-item-in-attr.stderr +++ b/tests/ui/attributes/unused-item-in-attr.stderr @@ -1,6 +1,4 @@ -error: unexpected expression: `{ - extern crate alloc; - }` +error: attribute value must be a literal --> $DIR/unused-item-in-attr.rs:1:7 | LL | #[w = { extern crate alloc; }] diff --git a/tests/ui/consts/issue-90878-2.rs b/tests/ui/consts/issue-90878-2.rs index e5bcecce6ee..0e61c65305f 100644 --- a/tests/ui/consts/issue-90878-2.rs +++ b/tests/ui/consts/issue-90878-2.rs @@ -1,4 +1,4 @@ - #![l=|x|[b;x ]] //~ ERROR unexpected expression: `|x| [b; x]` + #![l=|x|[b;x ]] //~ ERROR attribute value must be a literal //~^ ERROR cannot find attribute `l` in this scope // notice the space at the start, diff --git a/tests/ui/consts/issue-90878-2.stderr b/tests/ui/consts/issue-90878-2.stderr index 71b8d21fb4d..0b332840042 100644 --- a/tests/ui/consts/issue-90878-2.stderr +++ b/tests/ui/consts/issue-90878-2.stderr @@ -1,4 +1,4 @@ -error: unexpected expression: `|x| [b; x]` +error: attribute value must be a literal --> $DIR/issue-90878-2.rs:1:7 | LL | #![l=|x|[b;x ]] diff --git a/tests/ui/malformed/malformed-interpolated.rs b/tests/ui/malformed/malformed-interpolated.rs index 0d84e723fc3..0d801ebd3ac 100644 --- a/tests/ui/malformed/malformed-interpolated.rs +++ b/tests/ui/malformed/malformed-interpolated.rs @@ -10,7 +10,7 @@ macro_rules! check { check!("0"); // OK check!(0); // OK check!(0u8); //~ ERROR suffixed literals are not allowed in attributes -check!(-0); //~ ERROR unexpected expression: `-0` -check!(0 + 0); //~ ERROR unexpected expression: `0 + 0` +check!(-0); //~ ERROR attribute value must be a literal +check!(0 + 0); //~ ERROR attribute value must be a literal fn main() {} diff --git a/tests/ui/malformed/malformed-interpolated.stderr b/tests/ui/malformed/malformed-interpolated.stderr index c24d9f15388..92e99b7a70b 100644 --- a/tests/ui/malformed/malformed-interpolated.stderr +++ b/tests/ui/malformed/malformed-interpolated.stderr @@ -6,13 +6,13 @@ LL | check!(0u8); | = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) -error: unexpected expression: `-0` +error: attribute value must be a literal --> $DIR/malformed-interpolated.rs:13:8 | LL | check!(-0); | ^^ -error: unexpected expression: `0 + 0` +error: attribute value must be a literal --> $DIR/malformed-interpolated.rs:14:8 | LL | check!(0 + 0); diff --git a/tests/ui/parser/bad-lit-suffixes.rs b/tests/ui/parser/bad-lit-suffixes.rs index 8cb9ef7e0c9..c614f493885 100644 --- a/tests/ui/parser/bad-lit-suffixes.rs +++ b/tests/ui/parser/bad-lit-suffixes.rs @@ -28,11 +28,12 @@ fn main() { } #[rustc_dummy = "string"suffix] -//~^ ERROR unexpected expression: `"string"suffix` +//~^ ERROR suffixes on string literals are invalid fn f() {} #[must_use = "string"suffix] -//~^ ERROR unexpected expression: `"string"suffix` +//~^ ERROR suffixes on string literals are invalid +//~| ERROR malformed `must_use` attribute input fn g() {} #[link(name = "string"suffix)] diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr index 756f99ab12c..b5dacdf7d0d 100644 --- a/tests/ui/parser/bad-lit-suffixes.stderr +++ b/tests/ui/parser/bad-lit-suffixes.stderr @@ -10,26 +10,39 @@ error: suffixes on string literals are invalid LL | "C"suffix | ^^^^^^^^^ invalid suffix `suffix` -error: unexpected expression: `"string"suffix` +error: suffixes on string literals are invalid --> $DIR/bad-lit-suffixes.rs:30:17 | LL | #[rustc_dummy = "string"suffix] - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ invalid suffix `suffix` -error: unexpected expression: `"string"suffix` +error: suffixes on string literals are invalid --> $DIR/bad-lit-suffixes.rs:34:14 | LL | #[must_use = "string"suffix] - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ invalid suffix `suffix` + +error: malformed `must_use` attribute input + --> $DIR/bad-lit-suffixes.rs:34:1 + | +LL | #[must_use = "string"suffix] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[must_use = "reason"] + | +LL | #[must_use] + | error: suffixes on string literals are invalid - --> $DIR/bad-lit-suffixes.rs:38:15 + --> $DIR/bad-lit-suffixes.rs:39:15 | LL | #[link(name = "string"suffix)] | ^^^^^^^^^^^^^^ invalid suffix `suffix` error: invalid suffix `suffix` for number literal - --> $DIR/bad-lit-suffixes.rs:42:41 + --> $DIR/bad-lit-suffixes.rs:43:41 | LL | #[rustc_layout_scalar_valid_range_start(0suffix)] | ^^^^^^^ invalid suffix `suffix` @@ -136,5 +149,5 @@ LL | 1.0e10suffix; | = help: valid suffixes are `f32` and `f64` -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/parser/issues/issue-104620.rs b/tests/ui/parser/issues/issue-104620.rs index f49476c4408..fd0916b4411 100644 --- a/tests/ui/parser/issues/issue-104620.rs +++ b/tests/ui/parser/issues/issue-104620.rs @@ -1,4 +1,4 @@ #![feature(rustc_attrs)] -#![rustc_dummy=5z] //~ ERROR unexpected expression: `5z` +#![rustc_dummy=5z] //~ ERROR invalid suffix `z` for number literal fn main() {} diff --git a/tests/ui/parser/issues/issue-104620.stderr b/tests/ui/parser/issues/issue-104620.stderr index fa20b5f8b16..040c63a5fbf 100644 --- a/tests/ui/parser/issues/issue-104620.stderr +++ b/tests/ui/parser/issues/issue-104620.stderr @@ -1,8 +1,10 @@ -error: unexpected expression: `5z` +error: invalid suffix `z` for number literal --> $DIR/issue-104620.rs:3:16 | LL | #![rustc_dummy=5z] - | ^^ + | ^^ invalid suffix `z` + | + = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) error: aborting due to 1 previous error -- cgit 1.4.1-3-g733a5 From e274372689928972e4e78a24d615f6c4d375f7d4 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 12 Dec 2023 13:33:39 +0100 Subject: Correctly gate the parsing of match arms without body --- compiler/rustc_ast_lowering/src/expr.rs | 7 +- compiler/rustc_ast_passes/messages.ftl | 4 + compiler/rustc_ast_passes/src/errors.rs | 9 ++ compiler/rustc_ast_passes/src/feature_gate.rs | 29 +++++- compiler/rustc_parse/src/parser/expr.rs | 7 +- .../feature-gates/feature-gate-never_patterns.rs | 65 ++++++++++-- .../feature-gate-never_patterns.stderr | 114 ++++++++++++++++++++- tests/ui/parser/match-arm-without-body.rs | 2 - tests/ui/parser/match-arm-without-body.stderr | 16 +-- 9 files changed, 227 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index e9f88d50937..a44b408feec 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -581,8 +581,11 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { // Either `body.is_none()` or `is_never_pattern` here. if !is_never_pattern { - let suggestion = span.shrink_to_hi(); - self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion }); + if self.tcx.features().never_patterns { + // If the feature is off we already emitted the error after parsing. + let suggestion = span.shrink_to_hi(); + self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion }); + } } else if let Some(body) = &arm.body { self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span }); guard = None; diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 876126b02ea..28bd6c2111b 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -174,6 +174,10 @@ ast_passes_item_underscore = `{$kind}` items in this context need a name ast_passes_keyword_lifetime = lifetimes cannot use keyword names +ast_passes_match_arm_with_no_body = + `match` arm with no body + .suggestion = add a body after the pattern + ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name .help = consider using the `#[path]` attribute to specify filesystem path diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 7f6fcb49317..928bf19759a 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -758,3 +758,12 @@ pub struct AnonStructOrUnionNotAllowed { pub span: Span, pub struct_or_union: &'static str, } + +#[derive(Diagnostic)] +#[diag(ast_passes_match_arm_with_no_body)] +pub struct MatchArmWithNoBody { + #[primary_span] + pub span: Span, + #[suggestion(code = " => todo!(),", applicability = "has-placeholders")] + pub suggestion: Span, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 897f35a5c4a..ac55c6cabd0 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -554,7 +554,34 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); - gate_all!(never_patterns, "`!` patterns are experimental"); + + if !visitor.features.never_patterns { + if let Some(spans) = spans.get(&sym::never_patterns) { + for &span in spans { + if span.allows_unstable(sym::never_patterns) { + continue; + } + let sm = sess.source_map(); + // We gate two types of spans: the span of a `!` pattern, and the span of a + // match arm without a body. For the latter we want to give the user a normal + // error. + if let Ok(snippet) = sm.span_to_snippet(span) + && snippet == "!" + { + feature_err( + &sess.parse_sess, + sym::never_patterns, + span, + "`!` patterns are experimental", + ) + .emit(); + } else { + let suggestion = span.shrink_to_hi(); + sess.emit_err(errors::MatchArmWithNoBody { span, suggestion }); + } + } + } + } if !visitor.features.negative_bounds { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3c0627526be..bdaf8db1311 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2918,7 +2918,12 @@ impl<'a> Parser<'a> { let mut result = if !is_fat_arrow && !is_almost_fat_arrow { // A pattern without a body, allowed for never patterns. arm_body = None; - this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]) + this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]).map( + |x| { + this.sess.gated_spans.gate(sym::never_patterns, pat.span); + x + }, + ) } else { if let Err(mut err) = this.expect(&token::FatArrow) { // We might have a `=>` -> `=` or `->` typo (issue #89396). diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs index ca5ce3b9489..97c68e460ea 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.rs +++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs @@ -12,16 +12,67 @@ fn main() { unsafe { let ptr: *const Void = NonNull::dangling().as_ptr(); match *ptr { - ! //~ ERROR `!` patterns are experimental + ! + //~^ ERROR `!` patterns are experimental + //~| ERROR `!` patterns are experimental + } + // Check that the gate operates even behind `cfg`. + #[cfg(FALSE)] + match *ptr { + ! + //~^ ERROR `!` patterns are experimental + //~| ERROR `!` patterns are experimental + } + #[cfg(FALSE)] + match *ptr { + ! => {} + //~^ ERROR `!` patterns are experimental } } + // Correctly gate match arms with no body. + match Some(0) { + None => {} + Some(_), + //~^ ERROR unexpected `,` in pattern + } + match Some(0) { + None => {} + Some(_) + //~^ ERROR `match` arm with no body + } + match Some(0) { + _ => {} + Some(_) if false, + //~^ ERROR `match` arm with no body + Some(_) if false + //~^ ERROR `match` arm with no body + } + match res { + Ok(_) => {} + Err(!), + //~^ ERROR `match` arm with no body + //~| ERROR `!` patterns are experimental + } + match res { + Err(!) if false, + //~^ ERROR `match` arm with no body + //~| ERROR a guard on a never pattern will never be run + //~| ERROR `!` patterns are experimental + _ => {} + } + // Check that the gate operates even behind `cfg`. - #[cfg(FALSE)] - unsafe { - let ptr: *const Void = NonNull::dangling().as_ptr(); - match *ptr { - ! => {} //~ ERROR `!` patterns are experimental - } + match Some(0) { + None => {} + #[cfg(FALSE)] + Some(_) + //~^ ERROR `match` arm with no body + } + match Some(0) { + _ => {} + #[cfg(FALSE)] + Some(_) if false + //~^ ERROR `match` arm with no body } } diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index 2354a3b0476..4ba80603d9f 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -1,3 +1,18 @@ +error: unexpected `,` in pattern + --> $DIR/feature-gate-never_patterns.rs:36:16 + | +LL | Some(_), + | ^ + | +help: try adding parentheses to match on a tuple... + | +LL | (Some(_),) + | + + +help: ...or a vertical bar to match on multiple alternatives + | +LL | Some(_) | + | + error[E0408]: variable `_x` is not bound in all patterns --> $DIR/feature-gate-never_patterns.rs:8:19 | @@ -25,7 +40,36 @@ LL | ! = help: add `#![feature(never_patterns)]` to the crate attributes to enable error[E0658]: `!` patterns are experimental - --> $DIR/feature-gate-never_patterns.rs:24:13 + --> $DIR/feature-gate-never_patterns.rs:15:13 + | +LL | ! + | ^ + | + = note: see issue #118155 for more information + = help: add `#![feature(never_patterns)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0658]: `!` patterns are experimental + --> $DIR/feature-gate-never_patterns.rs:22:13 + | +LL | ! + | ^ + | + = note: see issue #118155 for more information + = help: add `#![feature(never_patterns)]` to the crate attributes to enable + +error[E0658]: `!` patterns are experimental + --> $DIR/feature-gate-never_patterns.rs:22:13 + | +LL | ! + | ^ + | + = note: see issue #118155 for more information + = help: add `#![feature(never_patterns)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0658]: `!` patterns are experimental + --> $DIR/feature-gate-never_patterns.rs:28:13 | LL | ! => {} | ^ @@ -33,7 +77,73 @@ LL | ! => {} = note: see issue #118155 for more information = help: add `#![feature(never_patterns)]` to the crate attributes to enable -error: aborting due to 4 previous errors +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:41:9 + | +LL | Some(_) + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` + +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:46:9 + | +LL | Some(_) if false, + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` + +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:48:9 + | +LL | Some(_) if false + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` + +error[E0658]: `!` patterns are experimental + --> $DIR/feature-gate-never_patterns.rs:53:13 + | +LL | Err(!), + | ^ + | + = note: see issue #118155 for more information + = help: add `#![feature(never_patterns)]` to the crate attributes to enable + +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:53:9 + | +LL | Err(!), + | ^^^^^^- help: add a body after the pattern: `=> todo!(),` + +error[E0658]: `!` patterns are experimental + --> $DIR/feature-gate-never_patterns.rs:58:13 + | +LL | Err(!) if false, + | ^ + | + = note: see issue #118155 for more information + = help: add `#![feature(never_patterns)]` to the crate attributes to enable + +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:58:9 + | +LL | Err(!) if false, + | ^^^^^^- help: add a body after the pattern: `=> todo!(),` + +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:69:9 + | +LL | Some(_) + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` + +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:75:9 + | +LL | Some(_) if false + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` + +error: a guard on a never pattern will never be run + --> $DIR/feature-gate-never_patterns.rs:58:19 + | +LL | Err(!) if false, + | ^^^^^ help: remove this guard + +error: aborting due to 18 previous errors Some errors have detailed explanations: E0408, E0658. For more information about an error, try `rustc --explain E0408`. diff --git a/tests/ui/parser/match-arm-without-body.rs b/tests/ui/parser/match-arm-without-body.rs index c3487c2c658..4723abff8b6 100644 --- a/tests/ui/parser/match-arm-without-body.rs +++ b/tests/ui/parser/match-arm-without-body.rs @@ -66,8 +66,6 @@ fn main() { pat!() //~^ ERROR expected `,` following `match` arm //~| HELP missing a comma here - //~| ERROR `match` arm with no body - //~| HELP add a body after the pattern _ => {} } match Some(false) { diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr index 3a06ed050b5..d98c7ec2826 100644 --- a/tests/ui/parser/match-arm-without-body.stderr +++ b/tests/ui/parser/match-arm-without-body.stderr @@ -68,19 +68,19 @@ error: `match` arm with no body --> $DIR/match-arm-without-body.rs:30:9 | LL | Some(_) if true - | ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),` + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error: `match` arm with no body --> $DIR/match-arm-without-body.rs:40:9 | LL | Some(_) if true, - | ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),` + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error: `match` arm with no body --> $DIR/match-arm-without-body.rs:45:9 | LL | Some(_) if true, - | ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),` + | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error: `match` arm with no body --> $DIR/match-arm-without-body.rs:51:9 @@ -98,19 +98,13 @@ error: `match` arm with no body --> $DIR/match-arm-without-body.rs:61:9 | LL | pat!() if true, - | ^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),` - -error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:66:9 - | -LL | pat!() | ^^^^^^- help: add a body after the pattern: `=> todo!(),` error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:74:9 + --> $DIR/match-arm-without-body.rs:72:9 | LL | pat!(), | ^^^^^^- help: add a body after the pattern: `=> todo!(),` -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 19e0c984d3ff44c6e273ddae2f327e8ad8726fae Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 12 Dec 2023 14:52:05 +0100 Subject: Don't gate the feature twice --- compiler/rustc_ast/src/ast.rs | 13 +++++ compiler/rustc_parse/src/parser/expr.rs | 5 +- .../feature-gates/feature-gate-never_patterns.rs | 8 +--- .../feature-gate-never_patterns.stderr | 56 +++++----------------- 4 files changed, 31 insertions(+), 51 deletions(-) (limited to 'tests') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 190fae95652..5755ae8a8bc 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -676,6 +676,19 @@ impl Pat { }); could_be_never_pattern } + + /// Whether this contains a `!` pattern. This in particular means that a feature gate error will + /// be raised if the feature is off. Used to avoid gating the feature twice. + pub fn contains_never_pattern(&self) -> bool { + let mut contains_never_pattern = false; + self.walk(&mut |pat| { + if matches!(pat.kind, PatKind::Never) { + contains_never_pattern = true; + } + true + }); + contains_never_pattern + } } /// A single field in a struct pattern. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index bdaf8db1311..5b0011e9f70 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2920,7 +2920,10 @@ impl<'a> Parser<'a> { arm_body = None; this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]).map( |x| { - this.sess.gated_spans.gate(sym::never_patterns, pat.span); + // Don't gate twice + if !pat.contains_never_pattern() { + this.sess.gated_spans.gate(sym::never_patterns, pat.span); + } x }, ) diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs index 97c68e460ea..f3910622313 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.rs +++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs @@ -14,14 +14,12 @@ fn main() { match *ptr { ! //~^ ERROR `!` patterns are experimental - //~| ERROR `!` patterns are experimental } // Check that the gate operates even behind `cfg`. #[cfg(FALSE)] match *ptr { ! //~^ ERROR `!` patterns are experimental - //~| ERROR `!` patterns are experimental } #[cfg(FALSE)] match *ptr { @@ -51,14 +49,12 @@ fn main() { match res { Ok(_) => {} Err(!), - //~^ ERROR `match` arm with no body - //~| ERROR `!` patterns are experimental + //~^ ERROR `!` patterns are experimental } match res { Err(!) if false, - //~^ ERROR `match` arm with no body + //~^ ERROR `!` patterns are experimental //~| ERROR a guard on a never pattern will never be run - //~| ERROR `!` patterns are experimental _ => {} } diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index 4ba80603d9f..dd10829d495 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -1,5 +1,5 @@ error: unexpected `,` in pattern - --> $DIR/feature-gate-never_patterns.rs:36:16 + --> $DIR/feature-gate-never_patterns.rs:34:16 | LL | Some(_), | ^ @@ -40,17 +40,7 @@ LL | ! = help: add `#![feature(never_patterns)]` to the crate attributes to enable error[E0658]: `!` patterns are experimental - --> $DIR/feature-gate-never_patterns.rs:15:13 - | -LL | ! - | ^ - | - = note: see issue #118155 for more information - = help: add `#![feature(never_patterns)]` to the crate attributes to enable - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0658]: `!` patterns are experimental - --> $DIR/feature-gate-never_patterns.rs:22:13 + --> $DIR/feature-gate-never_patterns.rs:21:13 | LL | ! | ^ @@ -59,17 +49,7 @@ LL | ! = help: add `#![feature(never_patterns)]` to the crate attributes to enable error[E0658]: `!` patterns are experimental - --> $DIR/feature-gate-never_patterns.rs:22:13 - | -LL | ! - | ^ - | - = note: see issue #118155 for more information - = help: add `#![feature(never_patterns)]` to the crate attributes to enable - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0658]: `!` patterns are experimental - --> $DIR/feature-gate-never_patterns.rs:28:13 + --> $DIR/feature-gate-never_patterns.rs:26:13 | LL | ! => {} | ^ @@ -78,25 +58,25 @@ LL | ! => {} = help: add `#![feature(never_patterns)]` to the crate attributes to enable error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:41:9 + --> $DIR/feature-gate-never_patterns.rs:39:9 | LL | Some(_) | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:46:9 + --> $DIR/feature-gate-never_patterns.rs:44:9 | LL | Some(_) if false, | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:48:9 + --> $DIR/feature-gate-never_patterns.rs:46:9 | LL | Some(_) if false | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error[E0658]: `!` patterns are experimental - --> $DIR/feature-gate-never_patterns.rs:53:13 + --> $DIR/feature-gate-never_patterns.rs:51:13 | LL | Err(!), | ^ @@ -104,14 +84,8 @@ LL | Err(!), = note: see issue #118155 for more information = help: add `#![feature(never_patterns)]` to the crate attributes to enable -error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:53:9 - | -LL | Err(!), - | ^^^^^^- help: add a body after the pattern: `=> todo!(),` - error[E0658]: `!` patterns are experimental - --> $DIR/feature-gate-never_patterns.rs:58:13 + --> $DIR/feature-gate-never_patterns.rs:55:13 | LL | Err(!) if false, | ^ @@ -120,30 +94,24 @@ LL | Err(!) if false, = help: add `#![feature(never_patterns)]` to the crate attributes to enable error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:58:9 - | -LL | Err(!) if false, - | ^^^^^^- help: add a body after the pattern: `=> todo!(),` - -error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:69:9 + --> $DIR/feature-gate-never_patterns.rs:65:9 | LL | Some(_) | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:75:9 + --> $DIR/feature-gate-never_patterns.rs:71:9 | LL | Some(_) if false | ^^^^^^^- help: add a body after the pattern: `=> todo!(),` error: a guard on a never pattern will never be run - --> $DIR/feature-gate-never_patterns.rs:58:19 + --> $DIR/feature-gate-never_patterns.rs:55:19 | LL | Err(!) if false, | ^^^^^ help: remove this guard -error: aborting due to 18 previous errors +error: aborting due to 14 previous errors Some errors have detailed explanations: E0408, E0658. For more information about an error, try `rustc --explain E0408`. -- cgit 1.4.1-3-g733a5 From 41c940413338f5364f834e67c1008e7823d6b5ef Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 12 Dec 2023 15:48:12 +0100 Subject: tests: CGU tests require build-pass, not check-pass (remove FIXME) CGU tests require CGU code to be exercised. We can't merely do "cargo check" on these tests. --- tests/incremental/thinlto/cgu_invalidated_via_import.rs | 2 +- tests/incremental/thinlto/cgu_keeps_identical_fn.rs | 2 +- tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/incremental/thinlto/cgu_invalidated_via_import.rs b/tests/incremental/thinlto/cgu_invalidated_via_import.rs index 5fe435d796f..e0cd385eff3 100644 --- a/tests/incremental/thinlto/cgu_invalidated_via_import.rs +++ b/tests/incremental/thinlto/cgu_invalidated_via_import.rs @@ -4,7 +4,7 @@ // revisions: cfail1 cfail2 cfail3 // compile-flags: -Z query-dep-graph -O -// build-pass (FIXME(62277): could be check-pass?) +// build-pass #![feature(rustc_attrs)] #![crate_type="rlib"] diff --git a/tests/incremental/thinlto/cgu_keeps_identical_fn.rs b/tests/incremental/thinlto/cgu_keeps_identical_fn.rs index 368a726ea90..781aae578d4 100644 --- a/tests/incremental/thinlto/cgu_keeps_identical_fn.rs +++ b/tests/incremental/thinlto/cgu_keeps_identical_fn.rs @@ -5,7 +5,7 @@ // revisions: cfail1 cfail2 cfail3 // compile-flags: -Z query-dep-graph -O -// build-pass (FIXME(62277): could be check-pass?) +// build-pass #![feature(rustc_attrs)] #![crate_type = "rlib"] diff --git a/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs b/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs index 045f2011958..8aa036ec978 100644 --- a/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs +++ b/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs @@ -3,7 +3,7 @@ // revisions: cfail1 cfail2 cfail3 // compile-flags: -Z query-dep-graph -O -// build-pass (FIXME(62277): could be check-pass?) +// build-pass #![feature(rustc_attrs)] #![crate_type="rlib"] -- cgit 1.4.1-3-g733a5