Repository navigation
Fix cfg_select! parser recovery handling and parser session error count leak - #7171
Conversation
|
Note that |
|
|
||
| if parser.psess.dcx().has_errors().is_some() { | ||
| parser.psess.dcx().reset_err_count(); | ||
| debug!("cfg entry parsed with recovery in cfg_select! predicate"); |
There was a problem hiding this comment.
could/should we propagate these parser errors? So the user knows we gave up because of invalid code
Conversely: do we ever want to not reset the error count like this? In which case could we just push it up a level
There was a problem hiding this comment.
could/should we propagate these parser errors? So the user knows we gave up because of invalid code
It depends. The problem is that, just because say a cfg_select! arm body is not "valid syntax" outside of macro context, it doesn't mean the overall macro itself is invalid syntax. Consider precisely
cfg_select! { unix => { fn foo {} } }fn foo {} is not valid syntax outside of macro contexts, but the overall cfg_select! is valid syntax. (If that makes sense.)
I don't think it makes sense to emit the give up either, not even under show_parse_errors=true, because macro formatting is essentially conditioned on a set of heuristics of "does this look like non-macro context rust code". That typically involves us synthetically invoking parts of rustc's parser. Unless under some hypothetical debug_why_rustfmt_gives_up_on_macros=true, then maybe.
Conversely: do we ever want to not reset the error count like this? In which case could we just push it up a level
Probably. I'll check.
There was a problem hiding this comment.
I pushed the error count up a level.
I'll probably look into a less footgun-ish approach in a follow-up #7180.
|
@rustbot author |
Since I didn't want to use a tempdir/tempfile just for piping a simple source file.
This ICEs on latest main.
This has to be a binary test not the usual {source, target} test, since
the latter doesn't have an installed ICE hook and the test would just
vacuously pass.
…nt leak Since rustc's parser session state is global. We don't reset it properly in `parse_items_from_cfg_select_inner`, so an valid `cfg_select!` followed by an invalid `cfg_select!` can inhibit formatting.
rustc's parser can produce an `Ok(expr)` with recoveries in the form of
diagnostics even if the `expr` is technically invalid. One such case is
an fn item that forgot the arg list parentheses, i.e. `fn foo {}`.
To properly handle this, we check for actual parser errors and abort if
there are parser errors (i.e. we re-emit the original macro invocation
unchanged).
ICE reported in <rust-lang#7087>.
We also reset error counts to avoid tainting other parsing sessions.
Since error count is a parser session global state, this was
accidentally leaking previously. For example, if you have a
`cfg_select!` containing valid item syntax followed by an `cfg_select!`
containing invalid item syntax, the invalid `cfg_select!` instance
unintentionally inhibits the formatting of the earlier valid one.
So fix this by properly resetting error counts.
f2a59a1 to
14c3546
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
| // NB: parser recovery can return `Ok(expr)` (or other non-terminals) and still emit errors. We need | ||
| // to properly reset the shared error count. |
There was a problem hiding this comment.
Note: I believe cfg_if! also has this error count leak bug, cf. #7184, I'll look into that later.
|
Changes since last review:
r? matthewhughes934 (since you're already reviewing) @rustbot review |
|
(I know there's a bit of duplicate code for the panic handling, I'll probably pull out a helper when I look into |
Overview
This PR fixes two bugs:
cfg_select!parsing which cause us to ICE, cf. [ICE]: bad span:):;cfg_select!invalid arm expr "successfully" formats but produces an ICE report #7087, andcfg_select!parsing where we unintentionally leak parser session error count. This can unintentionally inhibit formatting of another eligiblecfg_select!invocation.Fixes #7087.
Bug 1: incorrect parser recovery handling
Reported in #7087. rustc's parser can produce recoveries in the form of an
Ok(expr)(and other non-terminals) alongside diagnostics even if theexpris technically invalid. One such case is an function item that forgot the argument list parentheses, i.e.fn foo {}. That mistake pattern is common enough that the parser has recovery for it. Indeed,To properly handle this, we check for actual parser errors and bail if there are parser errors (i.e. we re-emit the original macro invocation unchanged). We also reset error counts to avoid tainting other parsing calls.
Bug 2: parser session error count leak
We use the same rustc parser session when trying to parse multiple
cfg_select!in the same file. The error count is shared state on said parser session. We don't properly reset the error count when we encounter an recovery inparse_items_from_cfg_select_innerwhich causes examples liketo give up (note first instance is valid body, second is invalid body) and not format the first instance.
To fix this, we need to properly reset error counts upon encountering potential recoveries.
Notes on testing strategy
Notes for the reviewer
I suggest the reviewer checkout the test commits specifically and observe outcome locally.
Changelog
):;cfg_select!invalid arm expr "successfully" formats but produces an ICE report #7087 was authored by me. I used LLM to help review my changes, since the parser recovery paths has some hard to notice edge cases.cfg_select!-with-invalid-item case, but also found thatparse_items_from_cfg_select_innerhas a slightly different parser session error count leak bug. I didn't notice this during the core fix, but indeed I double-checked this by hand (in the form of the test) and confirm that there is indeed a bug here. (I elaborate on the analysis earlier.)