Skip to content

Improve parse errors for stray lifetimes in type position #139854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 17, 2025

Conversation

fmease
Copy link
Member

@fmease fmease commented Apr 15, 2025

While technically & syntactically speaking lifetimes do begin1 types in type contexts (this essentially excludes generic argument lists) and require a following + to form a complete type ('a + denotes a bare trait object type), the likelihood that a user meant to write a lifetime-prefixed bare trait object type in modern editions (Rust ≥2021) when placing a lifetime into a type context is incredibly low (they would need to add at least three tokens to turn it into a semantically well-formed TOT: 'adyn 'a + Trait).

Therefore let's lie in modern editions (just like in PR #131239, a precedent if you will) by stating "expected type, found lifetime" in such cases which is a lot more a approachable, digestible and friendly compared to "lifetime in trait object type must be followed by +" (as added in PR #69760).

I've also added recovery for "ampersand-less" reference types (e.g., 'a (), 'a mut Ty) in modern editions because it was trivial to do and I think it's not unlikely to occur in practice.

Fixes #133413.

Footnotes

  1. For example, in the context of decl macros, this implies that a lone 'a always matches syntax fragment ty ("even if" there's a later macro matcher expecting syntax fragment lifetime). Rephrased, lifetimes (in type contexts) commit to the type parser.

fmease added 2 commits April 15, 2025 10:08
Namely, use a more sensical primary span.
Don't pretty-print AST nodes for the diagnostic message. Why:
* It's lossy (e.g., it doesn't replicate trailing `+`s in trait objects.
* It's prone to leak error nodes (printed as `(/*ERROR*/)`) since
  the LHS can easily represent recovered code (e.g., `fn(i32?) + T`).
@rustbot
Copy link
Collaborator

rustbot commented Apr 15, 2025

r? @davidtwco

rustbot has assigned @davidtwco.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 15, 2025
@fmease fmease changed the title Improve parse errors for lifetimes in type position Improve parse errors for stray lifetimes in type position Apr 15, 2025
// In Rust 2021 and beyond, we assume that the user didn't intend to write a bare trait
// object type with a leading lifetime bound since that seems very unlikely given the
// fact that `dyn`-less trait objects are *semantically* invalid.
if self.psess.edition.at_least_rust_2021() {
Copy link
Member Author

@fmease fmease Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to dropping the edition check making us emit the new message and output TyKind::Error in all editions (undoing parts of PR #69760), thereby removing NeedPlusAfterTraitObjectLifetime entirely.

Copy link
Member Author

@fmease fmease Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to also suggesting dyn 'a + /* Trait */ when facing 'a in modern editions, too.

@rust-log-analyzer

This comment has been minimized.

@fmease fmease force-pushed the modern-diag-for-lt-in-ty branch from e9e8414 to f8c5436 Compare April 15, 2025 09:29
Comment on lines +414 to +416
// For `('a) + …`, we know that `'a` in type position already lead to an error being
// emitted. To reduce output, let's indirectly suppress E0178 (bad `+` in type) and
// other irrelevant consequential errors.
Copy link
Member Author

@fmease fmease Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an explainer for what's already going on here.

In commit fmease@f8c5436 which I've since removed from this PR, I've experimented with removing this which would've allowed us to suggest removing parentheses in maybe_recover_from_bad_type_plus for type ('a) + Bounds… (that's a FIXME in tests/ui/parser/trait-object-lifetime-parens.rs) but that made things worse (more verbose output, nasty boolean flag, still false-positives (e.g., (?'a) + Bound where ? is invalid but we recover which we can't detect later)).

@fmease fmease force-pushed the modern-diag-for-lt-in-ty branch from f8c5436 to 6242335 Compare April 15, 2025 09:34
Copy link
Member

@davidtwco davidtwco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@davidtwco
Copy link
Member

@bors r+

@bors
Copy link
Collaborator

bors commented Apr 16, 2025

📌 Commit 6242335 has been approved by davidtwco

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 16, 2025
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 17, 2025
…iaskrgr

Rollup of 9 pull requests

Successful merges:

 - rust-lang#135340 (Add `explicit_extern_abis` Feature and Enforce Explicit ABIs)
 - rust-lang#139440 (rustc_target: RISC-V: feature addition batch 2)
 - rust-lang#139667 (cfi: Remove #[no_sanitize(cfi)] for extern weak functions)
 - rust-lang#139828 (Don't require rigid alias's trait to hold)
 - rust-lang#139854 (Improve parse errors for stray lifetimes in type position)
 - rust-lang#139889 (Clean UI tests 3 of n)
 - rust-lang#139894 (Fix `opt-dist` CLI flag and make it work without LLD)
 - rust-lang#139900 (stepping into impls for normalization is unproductive)
 - rust-lang#139915 (replace some #[rustc_intrinsic] usage with use of the libcore declarations)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 7ab385e into rust-lang:master Apr 17, 2025
6 checks passed
@rustbot rustbot added this to the 1.88.0 milestone Apr 17, 2025
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Apr 17, 2025
Rollup merge of rust-lang#139854 - fmease:modern-diag-for-lt-in-ty, r=davidtwco

Improve parse errors for stray lifetimes in type position

While technically & syntactically speaking lifetimes do begin[^1] types in type contexts (this essentially excludes generic argument lists) and require a following `+` to form a complete type (`'a +` denotes a bare trait object type), the likelihood that a user meant to write a lifetime-prefixed bare trait object type in *modern* editions (Rust ≥2021) when placing a lifetime into a type context is incredibly low (they would need to add at least three tokens to turn it into a *semantically* well-formed TOT: `'a` → `dyn 'a + Trait`).

Therefore let's *lie* in modern editions (just like in PR rust-lang#131239, a precedent if you will) by stating "*expected type, found lifetime*" in such cases which is a lot more a approachable, digestible and friendly compared to "*lifetime in trait object type must be followed by `+`*" (as added in PR rust-lang#69760).

I've also added recovery for "ampersand-less" reference types (e.g., `'a ()`, `'a mut Ty`) in modern editions because it was trivial to do and I think it's not unlikely to occur in practice.

Fixes rust-lang#133413.

[^1]: For example, in the context of decl macros, this implies that a lone `'a` always matches syntax fragment `ty` ("even if" there's a later macro matcher expecting syntax fragment `lifetime`). Rephrased, lifetimes (in type contexts) *commit* to the type parser.
@fmease fmease deleted the modern-diag-for-lt-in-ty branch April 17, 2025 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Confusing diagnostic for stray lifetime in type
5 participants