-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
disambiguate between multiple suggestions and a single multi-span suggestion; or, JSON error format is not round-trippable #53934
Comments
I totally forgot about this :/ I've been wanting to fix this in forever.
That won't work in the general case as the suggestions might be two completely different solution paths that require changing independent code. Clippy has such a suggestion. I propose we change multipart suggestions to each be a single element of the They are inherently broken this way, so noone could have used them. Thus I believe we can break this. We cannot just add some new annotation to the json, because there might be multiple multipart suggestions, and these are even worse as they produce a big flattened list of parts. |
Just a note, for tools (like IDEs) where you manually apply suggestions, it is not a problem, because the user sees the choices and decides which to use. So AFAIK in that scenario it is not broken. I'm not opposed to breaking it, since I can update my tool, but other tool developers may not be aware and changes may affect them. If you do change the structure, it would be helpful if the PR includes a very clear description of the changes to the json output. I've been wanting to document the JSON format for a while, so maybe this will motivate me to do it! 😄 |
If we change the behaviour, they will just see fewer suggestions, which isn't really breaking. |
I don't understand how multiple multi-part suggestions rules out adding new annotations as a solution? Suppose that in addition to "suggested_replacement" and "suggestion_applicability", |
RFC 2093 (tracking issue rust-lang#44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang/rustfix#141 (and, causally upstream of that, rust-lang#53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns rust-lang#52042.
It would be nice to demonstrate the shining correctness here with more run-rustfix tests than this, but unfortunately, that doesn't work with multipart suggestions yet (rust-lang#53934). While we're here, reword the zero-use lifetime suggestion to "elide the unused lifetime" instead of "remove it". (It's classier.)
single life * structured ~~autofixable~~ (well, pending #53934 and rust-lang/rustfix#141) suggestions for the single-use-lifetimes lint in the case of function and method reference args * don't consider the anonymous lifetime `'_` as "single-use" (it's intended for exactly this sort of thing) ![single_life](https://user-images.githubusercontent.com/1076988/47613227-3b2b6400-da48-11e8-8efd-cb975ddf537d.png) r? @nikomatsakis
Hm, @pietroalbini has a new patch in rustfix and open PR for a rustc lint change that uses |
I'd say it's a bug in my PR |
Thanks for catching that, @zackmdavis! This use case totally slipped my mind! Talking with @pietroalbini about this on discord in #rustfix right now |
So, yeah, what we should do to fix this? I'd be willing to do the implementation work. |
I think a possible solution would have for multiple suggestions to be considered multiple children. That way, the presented example would remain as is (and tools would have to act accordingly, applying all suggested changes simultaneously, and for cases like "struct not found, import one of the following" being presented as different
Does anyone see a problem with making this change? The only thing I can think of is tools that are updated to interpret the json output in that way would still receive the output from older |
Of course, the above is proposed as a way to keep as much backwards and forwards compatibility as possible. It would still break down in that people might still be able to apply, individually, all of the alternative suggestions in the same place, where really only one should. My ideal change would be to completely rework the output to raise suggestions as a fully supported concept, that could communicate "multiple multipart suggestions" without having to fake it. |
I would prefer to avoid using multiple children. Currently with multiple spans I'm able to show a succinct list of suggestions: If they were multiple children, they would show up as separate messages with all the extra text for each suggestion. Is there an example of a machine-applicable suggestion that has this behavior? It seems to me that if there are multiple suggestions where only one is valid, that should not be machine-applicable. I didn't fully understand the motivation for reverting @pietroalbini's PR. |
A CodeSuggestion can have multiple mutually-exclusive Substitutions, each of which has parts. Each CodeSuggestion becomes an element in the Current layout for diagnostic with one suggestion with multiple substitutions—
Current layout for diagnostic with one suggestion with single multi-part substitution—
This is bad because rustfix has no way to distinguish these two very different situations!! Proposed new layout for diagnostic with one suggestion, multiple substitutions—
That is, instead of each CodeSuggestion getting/becoming an element in the TODO—
|
This is meant to be the resolution to rust-lang#141 (and, in a way, rust-lang/rust#53934). We add two test fixtures demonstrating usage and correctness. The JSON comes from a locally-built non-master compiler featuring the change to the JSON emitter to support this. (Actually, one is from `--error-format pretty-json` and the other is from `--error-format json` and subsequent auto-formatting, but the difference in inter-string-literal newlines doesn't seem to matter.)
This checks in a baseline so that it's easy to see the diff when we change the output in a subsequent commit. These examples are from and for #53934.
I think so. Looking over Clippy, I found 3 lints that offer mutually exclusive suggestions. But they are not |
…ikomatsakis Clarify meaning of MachineApplicable suggestions. This documents the meaning of MachineApplicable in case of multiple suggestions, as described in rust-lang#53934 (comment) r? `@nikomatsakis`
…ikomatsakis Clarify meaning of MachineApplicable suggestions. This documents the meaning of MachineApplicable in case of multiple suggestions, as described in rust-lang#53934 (comment) r? ``@nikomatsakis``
@oli-obk and @estebank : Do you think that issue #53934 should remain at P-high? Or should it be downgraded to P-medium at this point, especially considering Niko’s writeup from May of 2021? |
(I discussed with @estebank ; we're going to leave this at P-high and they're going to see about enlisting some assistance to work on it.) |
@rustbot claim |
I was going to work on this but started a new job in February that took my spare time. I had started on this but didn't get much further than duplicating the error on my compiler build. Things have settled down now and I should have more time to get to this and I want to work more on rust in my spare time. If someone else wants to do it faster then I'm happy to let them do it, though. |
…rrect It is possible to have more than one valid suggestion, which when applied together via rustfix causes the code to no longer compile. This is a temporary workaround; the real long term solution to these issues is to solve <rust-lang#53934>.
…rrect It is possible to have more than one valid suggestion, which when applied together via rustfix causes the code to no longer compile. This is a temporary workaround; the real long term solution to these issues is to solve <rust-lang#53934>.
…rrect It is possible to have more than one valid suggestion, which when applied together via rustfix causes the code to no longer compile. This is a temporary workaround; the real long term solution to these issues is to solve <rust-lang#53934>.
…rrect It is possible to have more than one valid suggestion, which when applied together via rustfix causes the code to no longer compile. This is a temporary workaround; the real long term solution to these issues is to solve <rust-lang#53934>.
…mparisons_suggestion, r=Nadrieril Downgrade ambiguous_wide_pointer_comparisons suggestions to MaybeIncorrect In certain cases like rust-lang#121330, it is possible to have more than one suggestion from the `ambiguous_wide_pointer_comparisons` lint (which before this PR are `MachineApplicable`). When this gets passed to rustfix, rustfix makes *multiple* changes according to the suggestions which result in incorrect code. This is a temporary workaround. The real long term solution to problems like these is to address <rust-lang#53934>. This PR also includes a drive-by edit to the panic message emitted by compiletest because "ui" test suite now uses `//`@`` directives. Fixes rust-lang#121330.
Rollup merge of rust-lang#121338 - jieyouxu:ambiguous_wide_pointer_comparisons_suggestion, r=Nadrieril Downgrade ambiguous_wide_pointer_comparisons suggestions to MaybeIncorrect In certain cases like rust-lang#121330, it is possible to have more than one suggestion from the `ambiguous_wide_pointer_comparisons` lint (which before this PR are `MachineApplicable`). When this gets passed to rustfix, rustfix makes *multiple* changes according to the suggestions which result in incorrect code. This is a temporary workaround. The real long term solution to problems like these is to address <rust-lang#53934>. This PR also includes a drive-by edit to the panic message emitted by compiletest because "ui" test suite now uses `//`@`` directives. Fixes rust-lang#121330.
…anglo Add `CodeFix::apply_solution` and impl `Clone` ### What does this PR try to resolve? In Clippy we have a good few lints that produce mutually exclusive suggestions e.g. ``` error: octal-looking escape in a literal --> tests/ui/octal_escapes.rs:6:19 | LL | let _bad2 = b"\033[0m"; | ^^^^ | help: if an octal escape is intended, use a hex escape instead | LL | let _bad2 = b"\x1b[0m"; | ~~~~ help: if a null escape is intended, disambiguate using | LL | let _bad2 = b"\x0033[0m"; | ~~~~~~ ``` For these we have to disable rustfix tests since the suggestions are overlapping, this PR adds a method to `rustfix` that `ui_test` could use in order to produce multiple `.fixed` files, one for each alternative suggestion ### Additional information It does not work for for multiple suggestions coming from a single subdiagnostic ([`Diag::span_suggestions`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic/struct.Diag.html#method.span_suggestions)) e.g. ``` help: consider importing one of these items | 1 + use std::collections::HashMap; | 1 + use ahash::HashMap; ``` Solving this would be blocked on rust-lang/rust#53934, on the Clippy side we only have one use of `span_suggestions` however so it's still very useful without this The test cases use Clippy lints that I generated by setting the `parse_and_replace` test to use `clippy-driver` because of familiarity, if there's a rustc case that does multiple suggestions it would be good to go with that
Summary
To solve rust-lang/rustfix#141 (and one can only assume that RLS faces a similar problem), we need to be able to tell the difference between multiple suggestions (of which we likely only want to apply one), and a single suggestion that happens to touch multiple spans. The
DiagnosticBuilder
API supports this distinction by offering separatespan_suggestions
andmultipart_suggestion
methods. However, it looks like the actual JSON output conflates these two cases?! (I hope I've simply misunderstood something; @estebank @oli-obk @killercup @nrc, please tell I'm just being stupid and wrong and confused here; the embarrassment of that would be less bad than than the headache of having to fix this.)Diagnosis
The relevant layout of fields is this:
Diagnostic
has a vec of manyCodeSuggestion
s, aCodeSuggestion
has a vec of manySubstitution
s, and aSubstitution
has a vec of manySubstitutionPart
s.span_suggestions
pushes oneCodeSuggestion
with multipleSubstitution
s (each of which has a singleSubstitutionPart
) onto an existing diagnostic-builder. (So, arguably either the method namespan_suggestions
or the field namesuggestions
is a misnomer—it's the "substitutions" that are plural here, not the "suggestions"; you'd have to call.span_suggestion
multiple times to get multiple elements insuggestions
. But leave that aside for the moment.)multipart_suggestion
pushes oneCodeSuggestion
with oneSubstitution
with multipleSubstitutionParts
onto an existing diagnostic-builder.All this is fine. The problem comes when we serialize diagnostics to JSON for
--error-format json
. The serialized diagnostic format contains achildren
field whose elements are also serialized diagnostics (with no children themselves). The suggestions are converted and included as "children", but in doing so, we flat-map all the substitution parts together, making it impossible to know with certainty which parts came from the sameSubstitution
.Concrete examples
The following program (taken from the rustfix test suite, but let's call it
ambiguous-display.rs
here) fails to compile becauseDisplay
is not in scope. (Actually, it'll still fail after fixing that, but that doesn't matter for our purpose here.)We get two mutually-exclusive suggestions to use
std::fmt::Display
andstd::path::Display
, issued in librustc_resolve.The terminal error output is:
The JSON error format is:
The following program (
dot-dot-not-last.rs
) will fail to compile because..
can only appear last in a struct pattern.We get one unique suggestion that needs to touch multiple spans (removing the
..
from its original location and inserting it at the end), issued in the parser.The terminal error output is:
The JSON error output is:
We'd want Rustfix (and similar tools) to apply both of the suggestions in
children[0].spans
in the case of dot-dot-not-last.rs, but only one of them (offering a choice in an interactive mode) for ambiguous-display.rs. But how is Rustfix supposed to reliably tell the difference? (In the specific case ofspan_suggestions
, you can check that the start and end spans are the same inchildren[0].spans
, but I'd really rather not rely on that to merely infer something that the format, I argue, should state with certainty.)This issue should likely receive the A-diagnostics and T-dev-tools (and, one might argue, P-high) labels.
Current proposed resolution
This issue is currently proposed to be left as a future improvement; see this comment for the current status
The text was updated successfully, but these errors were encountered: