Don't emit unused_parens suggestion for proc-macro-synthesized parens around bounds#157662
Don't emit unused_parens suggestion for proc-macro-synthesized parens around bounds#157662sanidhyasin wants to merge 1 commit into
unused_parens suggestion for proc-macro-synthesized parens around bounds#157662Conversation
|
r? @mejrs rustbot has assigned @mejrs. Use Why was this reviewer chosen?The reviewer was selected based on:
|
This comment has been minimized.
This comment has been minimized.
3c1a008 to
d7243ee
Compare
|
Hi, thank your for your PR. I have a question regarding LLM tools, was and how LLMs been used to create this PR? |
Hey, thanks for taking a look! Yeah, I used Claude to help investigate the issue and find the PR that introduced the regression. It also helped me put together the fix and test. But I did go through it all myself and understand what's happening. The old code assumes the span always includes the surrounding parentheses, which isn't always true when a proc-macro reuses a span. In those cases, trimming the first and last byte can end up mangling the source and even ICE on multibyte characters. The fix just checks that the source actually starts and ends with parentheses before trying to lint it. Happy to change the approach if you think there's a better way to handle it. |
|
You can hit this with parser recovery as well by the way:
Using |
`poly_trait_ref.parens` only records that the parser saw parentheses around a trait-object/impl-trait bound; it does not guarantee that the bound's span actually points at those parentheses in the source. A proc-macro can synthesize the parentheses while reusing an unrelated span from its input, so the span may not be wrapped in parentheses at all. Previously the lint unconditionally trimmed the first and last byte of the span to build the "remove these parentheses" suggestion. On such reused spans this produced an invalid suggestion (e.g. rewriting a field `val: u8` to `al: u`) and could even ICE when the span started or ended on a multibyte character. Only emit the lint when the source text at the span really is wrapped in parentheses.
d7243ee to
66099cd
Compare
|
Hey, thanks for the example! Turns out the current check already handles that case. Those are fullwidth parens On I did look at dropping I also tightened the regression test while I was here: it was reusing the span of |
Fixes #144378.
The bug
When detecting unused parentheses around a trait-object /
impl Traitbound,UnusedParens::check_tybuilds the "remove these parentheses" suggestion by trimming the first and last byte ofpoly_trait_ref.span:poly_trait_ref.parens == Yesonly records that the parser saw parentheses around the bound — it does not guarantee thatspoints at them in the source. A proc-macro can synthesize the parentheses while reusing an unrelated span from its input (the reproducer in the issue does this viamodular-bitfield). On such a span the byte trimming:val: u8intoal: u, andchar).This is a regression from #142237, which introduced the bounds-detection path.
The fix
Only emit the lint when the source text at the span really is wrapped in parentheses, mirroring the robustness of the existing
TyKind::Parenarm. The(/)are ASCII, so once the snippet is confirmed to start/end with them the existing byte arithmetic is correct. Spans for genuine source parentheses are unchanged, so the existingunused_parensUI tests keep their exact output.Test
tests/ui/lint/unused/unused-parens-trait-obj-proc-macro-144378.rsuses a small proc-macro that emits&dyn (Send)while re-using the span of a multibyte identifier. Before this change it ICEd; now it compiles cleanly (check-pass).