Skip to content
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

Open
zackmdavis opened this issue Sep 3, 2018 · 29 comments
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-cleanup Category: PRs that clean code up or issues documenting cleanup. D-diagnostic-infra Diagnostics: Issues that affect all diagnostics, or relate to the diagnostic machinery itself. E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-diagnostics Working group: Diagnostics

Comments

@zackmdavis
Copy link
Member

zackmdavis commented Sep 3, 2018

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 separate span_suggestions and multipart_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 many CodeSuggestions, a CodeSuggestion has a vec of many Substitutions, and a Substitution has a vec of many SubstitutionParts.

span_suggestions pushes one CodeSuggestion with multiple Substitutions (each of which has a single SubstitutionPart) onto an existing diagnostic-builder. (So, arguably either the method name span_suggestions or the field name suggestions 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 in suggestions. But leave that aside for the moment.)

multipart_suggestion pushes one CodeSuggestion with one Substitution with multiple SubstitutionParts 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 a children 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 same Substitution.

Concrete examples

The following program (taken from the rustfix test suite, but let's call it ambiguous-display.rs here) fails to compile because Display is not in scope. (Actually, it'll still fail after fixing that, but that doesn't matter for our purpose here.)

fn main() {
    let xs = vec![String::from("foo")];
    let d: &Display = &xs;
    println!("{}", d);
}

We get two mutually-exclusive suggestions to use std::fmt::Display and std::path::Display, issued in librustc_resolve.

The terminal error output is:

error[E0412]: cannot find type `Display` in this scope
 --> ambiguous-display.rs:3:13
  |
3 |     let d: &Display = &xs;
  |             ^^^^^^^ not found in this scope
help: possible candidates are found in other modules, you can import them into scope
  |
1 | use std::fmt::Display;
  |
1 | use std::path::Display;

The JSON error format is:

{
    "message": "cannot find type `Display` in this scope",
    "code": {
        "code": "E0412",
        "explanation": "\nThe type name used is not in scope.\n\nErroneous code examples:\n\n```compile_fail,E0412\nimpl Something {} // error: type name `Something` is not in scope\n\n// or:\n\ntrait Foo {\n    fn bar(N); // error: type name `N` is not in scope\n}\n\n// or:\n\nfn foo(x: T) {} // type name `T` is not in scope\n```\n\nTo fix this error, please verify you didn't misspell the type name, you did\ndeclare it or imported it into the scope. Examples:\n\n```\nstruct Something;\n\nimpl Something {} // ok!\n\n// or:\n\ntrait Foo {\n    type N;\n\n    fn bar(_: Self::N); // ok!\n}\n\n// or:\n\nfn foo<T>(x: T) {} // ok!\n```\n\nAnother case that causes this error is when a type is imported into a parent\nmodule. To fix this, you can follow the suggestion and use File directly or\n`use super::File;` which will import the types from the parent namespace. An\nexample that causes this error is below:\n\n```compile_fail,E0412\nuse std::fs::File;\n\nmod foo {\n    fn some_function(f: File) {}\n}\n```\n\n```\nuse std::fs::File;\n\nmod foo {\n    // either\n    use super::File;\n    // or\n    // use std::fs::File;\n    fn foo(f: File) {}\n}\n# fn main() {} // don't insert it for us; that'll break imports\n```\n"
    },
    "level": "error",
    "spans": [
        {
            "file_name": "ambiguous-display.rs",
            "byte_start": 64,
            "byte_end": 71,
            "line_start": 3,
            "line_end": 3,
            "column_start": 13,
            "column_end": 20,
            "is_primary": true,
            "text": [
                {
                    "text": "    let d: &Display = &xs;",
                    "highlight_start": 13,
                    "highlight_end": 20
                }
            ],
            "label": "not found in this scope",
            "suggested_replacement": null,
            "suggestion_applicability": null,
            "expansion": null
        }
    ],
    "children": [
        {
            "message": "possible candidates are found in other modules, you can import them into scope",
            "code": null,
            "level": "help",
            "spans": [
                {
                    "file_name": "ambiguous-display.rs",
                    "byte_start": 0,
                    "byte_end": 0,
                    "line_start": 1,
                    "line_end": 1,
                    "column_start": 1,
                    "column_end": 1,
                    "is_primary": true,
                    "text": [
                        {
                            "text": "fn main() {",
                            "highlight_start": 1,
                            "highlight_end": 1
                        }
                    ],
                    "label": null,
                    "suggested_replacement": "use std::fmt::Display;\n\n",
                    "suggestion_applicability": "Unspecified",
                    "expansion": null
                },
                {
                    "file_name": "ambiguous-display.rs",
                    "byte_start": 0,
                    "byte_end": 0,
                    "line_start": 1,
                    "line_end": 1,
                    "column_start": 1,
                    "column_end": 1,
                    "is_primary": true,
                    "text": [
                        {
                            "text": "fn main() {",
                            "highlight_start": 1,
                            "highlight_end": 1
                        }
                    ],
                    "label": null,
                    "suggested_replacement": "use std::path::Display;\n\n",
                    "suggestion_applicability": "Unspecified",
                    "expansion": null
                }
            ],
            "children": [],
            "rendered": null
        }
    ],
    "rendered": "error[E0412]: cannot find type `Display` in this scope\n --> ambiguous-display.rs:3:13\n  |\n3 |     let d: &Display = &xs;\n  |             ^^^^^^^ not found in this scope\nhelp: possible candidates are found in other modules, you can import them into scope\n  |\n1 | use std::fmt::Display;\n  |\n1 | use std::path::Display;\n  |\n\n"
}

The following program (dot-dot-not-last.rs) will fail to compile because .. can only appear last in a struct pattern.

struct Point { x: isize, y: isize }

fn main() {
    let p = Point { x: 1, y: 2 };
    let Point { .., y, } = p;
}

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:

error: expected `}`, found `,`
 --> dot-dot-not-last.rs:5:19
  |
5 |     let Point { .., y, } = p;
  |                 --^
  |                 | |
  |                 | expected `}`
  |                 `..` must be at the end and cannot have a trailing comma
help: move the `..` to the end of the field list
  |
5 |     let Point {  y, .. } = p;
  |                --   ^^^^

The JSON error output is:

{
    "message": "expected `}`, found `,`",
    "code": null,
    "level": "error",
    "spans": [
        {
            "file_name": "dot-dot-not-last.rs",
            "byte_start": 101,
            "byte_end": 102,
            "line_start": 5,
            "line_end": 5,
            "column_start": 19,
            "column_end": 20,
            "is_primary": true,
            "text": [
                {
                    "text": "    let Point { .., y, } = p;",
                    "highlight_start": 19,
                    "highlight_end": 20
                }
            ],
            "label": "expected `}`",
            "suggested_replacement": null,
            "suggestion_applicability": null,
            "expansion": null
        },
        {
            "file_name": "dot-dot-not-last.rs",
            "byte_start": 99,
            "byte_end": 102,
            "line_start": 5,
            "line_end": 5,
            "column_start": 17,
            "column_end": 20,
            "is_primary": false,
            "text": [
                {
                    "text": "    let Point { .., y, } = p;",
                    "highlight_start": 17,
                    "highlight_end": 20
                }
            ],
            "label": "`..` must be at the end and cannot have a trailing comma",
            "suggested_replacement": null,
            "suggestion_applicability": null,
            "expansion": null
        }
    ],
    "children": [
        {
            "message": "move the `..` to the end of the field list",
            "code": null,
            "level": "help",
            "spans": [
                {
                    "file_name": "dot-dot-not-last.rs",
                    "byte_start": 99,
                    "byte_end": 102,
                    "line_start": 5,
                    "line_end": 5,
                    "column_start": 17,
                    "column_end": 20,
                    "is_primary": true,
                    "text": [
                        {
                            "text": "    let Point { .., y, } = p;",
                            "highlight_start": 17,
                            "highlight_end": 20
                        }
                    ],
                    "label": null,
                    "suggested_replacement": "",
                    "suggestion_applicability": "Unspecified",
                    "expansion": null
                },
                {
                    "file_name": "dot-dot-not-last.rs",
                    "byte_start": 106,
                    "byte_end": 107,
                    "line_start": 5,
                    "line_end": 5,
                    "column_start": 24,
                    "column_end": 25,
                    "is_primary": true,
                    "text": [
                        {
                            "text": "    let Point { .., y, } = p;",
                            "highlight_start": 24,
                            "highlight_end": 25
                        }
                    ],
                    "label": null,
                    "suggested_replacement": ".. }",
                    "suggestion_applicability": "Unspecified",
                    "expansion": null
                }
            ],
            "children": [],
            "rendered": null
        }
    ],
    "rendered": "error: expected `}`, found `,`\n --> dot-dot-not-last.rs:5:19\n  |\n5 |     let Point { .., y, } = p;\n  |                 --^\n  |                 | |\n  |                 | expected `}`\n  |                 `..` must be at the end and cannot have a trailing comma\nhelp: move the `..` to the end of the field list\n  |\n5 |     let Point {  y, .. } = p;\n  |                --   ^^^^\n\n"
}

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 of span_suggestions, you can check that the start and end spans are the same in children[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

@csmoe csmoe added the A-diagnostics Area: Messages for errors, warnings, and lints label Sep 4, 2018
@oli-obk
Copy link
Contributor

oli-obk commented Sep 4, 2018

I totally forgot about this :/ I've been wanting to fix this in forever.

you can check that the start and end spans are the same in children[0].spans

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 children array and have them set their own children array with their parts instead of having that directly in the spans field.

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.

@ehuss
Copy link
Contributor

ehuss commented Sep 4, 2018

They are inherently broken this way, so noone could have used them. Thus I believe we can break this.

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! 😄

@oli-obk
Copy link
Contributor

oli-obk commented Sep 4, 2018

If we change the behaviour, they will just see fewer suggestions, which isn't really breaking.

@zackmdavis
Copy link
Member Author

@oli-obk

We cannot just add some new annotation to the json, because there might be multiple multipart suggestions

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", DiagnosticSpan also had a "suggestion_number" key, whose value started at 0 for spans belonging to the first suggestion, and incremented for subsequent suggestions. Am I missing some obvious reason why that wouldn't work? (Of course, this would be somewhat hideous—if we can get away with breaking things, your proposal ("multipart suggestions to each be a single element" ...) sounds OK to me.)

zackmdavis added a commit to zackmdavis/rust that referenced this issue Sep 28, 2018
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.
zackmdavis added a commit to zackmdavis/rust that referenced this issue Oct 28, 2018
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.)
bors added a commit that referenced this issue Nov 4, 2018
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
@zackmdavis
Copy link
Member Author

Hm, @pietroalbini has a new patch in rustfix and open PR for a rustc lint change that uses multipart_suggestion_ that doesn't change the emitter behavior. So I think that either (a) my analysis above is mistaken, or (b) rust-lang/rustfix#155 has a bug where it fails to distinguish between a single multipart suggestion and multiple distinct suggestions.

@pietroalbini
Copy link
Member

I'd say it's a bug in my PR :(
Didn't know about this issue, cc @killercup

@killercup
Copy link
Member

Thanks for catching that, @zackmdavis! This use case totally slipped my mind!

Talking with @pietroalbini about this on discord in #rustfix right now

@pietroalbini
Copy link
Member

So, yeah, what we should do to fix this? I'd be willing to do the implementation work.

@estebank
Copy link
Contributor

estebank commented Dec 9, 2018

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 children with the same message (everything else too really) and a different suggested_replacement:

{
    "message": "cannot find type `Display` in this scope",
    "code": {
        "code": "E0412",
        "explanation": "\nThe type name used is not in scope.\n\nErroneous code examples:\n\n```compile_fail,E0412\nimpl Something {} // error: type name `Something` is not in scope\n\n// or:\n\ntrait Foo {\n    fn bar(N); // error: type name `N` is not in scope\n}\n\n// or:\n\nfn foo(x: T) {} // type name `T` is not in scope\n```\n\nTo fix this error, please verify you didn't misspell the type name, you did\ndeclare it or imported it into the scope. Examples:\n\n```\nstruct Something;\n\nimpl Something {} // ok!\n\n// or:\n\ntrait Foo {\n    type N;\n\n    fn bar(_: Self::N); // ok!\n}\n\n// or:\n\nfn foo<T>(x: T) {} // ok!\n```\n\nAnother case that causes this error is when a type is imported into a parent\nmodule. To fix this, you can follow the suggestion and use File directly or\n`use super::File;` which will import the types from the parent namespace. An\nexample that causes this error is below:\n\n```compile_fail,E0412\nuse std::fs::File;\n\nmod foo {\n    fn some_function(f: File) {}\n}\n```\n\n```\nuse std::fs::File;\n\nmod foo {\n    // either\n    use super::File;\n    // or\n    // use std::fs::File;\n    fn foo(f: File) {}\n}\n# fn main() {} // don't insert it for us; that'll break imports\n```\n"
    },
    "level": "error",
    "spans": [
        {
            "file_name": "ambiguous-display.rs",
            "byte_start": 64,
            "byte_end": 71,
            "line_start": 3,
            "line_end": 3,
            "column_start": 13,
            "column_end": 20,
            "is_primary": true,
            "text": [
                {
                    "text": "    let d: &Display = &xs;",
                    "highlight_start": 13,
                    "highlight_end": 20
                }
            ],
            "label": "not found in this scope",
            "suggested_replacement": null,
            "suggestion_applicability": null,
            "expansion": null
        }
    ],
    "children": [
        {
            "message": "possible candidates are found in other modules, you can import them into scope",
            "code": null,
            "level": "help",
            "spans": [
                {
                    "file_name": "ambiguous-display.rs",
                    "byte_start": 0,
                    "byte_end": 0,
                    "line_start": 1,
                    "line_end": 1,
                    "column_start": 1,
                    "column_end": 1,
                    "is_primary": true,
                    "text": [
                        {
                            "text": "fn main() {",
                            "highlight_start": 1,
                            "highlight_end": 1
                        }
                    ],
                    "label": null,
                    "suggested_replacement": "use std::fmt::Display;\n\n",
                    "suggestion_applicability": "Unspecified",
                    "expansion": null
                }
            ],
            "children": [],
            "rendered": null
        },
        {
            "message": "possible candidates are found in other modules, you can import them into scope",
            "code": null,
            "level": "help",
            "spans": [
                {
                    "file_name": "ambiguous-display.rs",
                    "byte_start": 0,
                    "byte_end": 0,
                    "line_start": 1,
                    "line_end": 1,
                    "column_start": 1,
                    "column_end": 1,
                    "is_primary": true,
                    "text": [
                        {
                            "text": "fn main() {",
                            "highlight_start": 1,
                            "highlight_end": 1
                        }
                    ],
                    "label": null,
                    "suggested_replacement": "use std::path::Display;\n\n",
                    "suggestion_applicability": "Unspecified",
                    "expansion": null
                }
            ],
            "children": [],
            "rendered": null
        }
    ],
    "rendered": "error[E0412]: cannot find type `Display` in this scope\n --> ambiguous-display.rs:3:13\n  |\n3 |     let d: &Display = &xs;\n  |             ^^^^^^^ not found in this scope\nhelp: possible candidates are found in other modules, you can import them into scope\n  |\n1 | use std::fmt::Display;\n  |\n1 | use std::path::Display;\n  |\n\n"
}

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 rustc versions and would do the wrong thing. To avoid that we could add a field to the output signaling how to interpret the output. For rustc output that doesn't carry that signal, continue doing what we're doing now (even if incorrect, at least consistent).

@estebank
Copy link
Contributor

estebank commented Dec 9, 2018

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.

@ehuss
Copy link
Contributor

ehuss commented Dec 10, 2018

I would prefer to avoid using multiple children. Currently with multiple spans I'm able to show a succinct list of suggestions:

image

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.

@zackmdavis
Copy link
Member Author

A CodeSuggestion can have multiple mutually-exclusive Substitutions, each of which has parts. Each CodeSuggestion becomes an element in the children field in the serialized Diagnostic

Current layout for diagnostic with one suggestion with multiple substitutions—

  • children has one element
  • each of children[0].spans is one of the distinct, mutually-exclusive substitutions

Current layout for diagnostic with one suggestion with single multi-part substitution—

  • children has one element
  • each of children[0].spans is a part of the same 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—

  • children has multiple elements, one for each proposed substitution
  • children[i].spans are the parts of that subtitution

That is, instead of each CodeSuggestion getting/becoming an element in the children array of the serialized-Diagnostic, each Substitution gets/becomes an element of the children. For most diagnostics, this makes no difference (a typical CodeSuggestion only has one substitution). The only diagnostics that should change are those with multiple Substitutions, which should be pretty rare (only created by .span_suggestions, which is called six times in the rustc codebase), giving us a small backwards-incompatibility surface area.

TODO—

  • read rustfix/src/lib.rs and confirm that this makes sense in terms of rustfix's "Solutions" concept
  • code up this change to the JSON emitter
  • appropriate changes to rustfix
  • ask RLS folks if/how this impacts them
  • ping ehuss on if/how this impacts them
  • PR!!

@killercup
Copy link
Member

notes from all hands meeting

zackmdavis added a commit to zackmdavis/rustfix that referenced this issue Feb 6, 2019
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.)
zackmdavis added a commit that referenced this issue Feb 6, 2019
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.
@camsteffen
Copy link
Contributor

is the assertion that no lints presently exist that offer exclusive machine-applicable suggestions true?

I think so. Looking over Clippy, I found 3 lints that offer mutually exclusive suggestions. But they are not MachineApplicable. The lints are (FWIW) zero_prefixed_literal, clone_double_ref, misrefactored_assign_op.

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Jun 3, 2021
…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`
JohnTitor added a commit to JohnTitor/rust that referenced this issue Jun 3, 2021
…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``
@jackh726 jackh726 added the WG-diagnostics Working group: Diagnostics label Feb 1, 2022
@pnkfelix
Copy link
Member

pnkfelix commented Feb 2, 2022

@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?

@pnkfelix
Copy link
Member

pnkfelix commented Feb 2, 2022

(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.)

@pnkfelix pnkfelix added the E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. label Feb 2, 2022
@pnkfelix
Copy link
Member

pnkfelix commented Feb 2, 2022

Also, since @estebank has agreed to mentor someone on this, I'm going to tag it as E-mentor.

@rustbot label: +E-mentor

@yerke
Copy link
Contributor

yerke commented Feb 2, 2022

@rustbot claim

@pnkfelix
Copy link
Member

pnkfelix commented Dec 16, 2022

Visiting for P-high review

@estebank @yerke how is progress moving along here?

@pauleaster
Copy link

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.

@jyn514 jyn514 added the D-diagnostic-infra Diagnostics: Issues that affect all diagnostics, or relate to the diagnostic machinery itself. label Apr 17, 2023
jieyouxu added a commit to jieyouxu/rust that referenced this issue Feb 20, 2024
…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>.
jieyouxu added a commit to jieyouxu/rust that referenced this issue Feb 20, 2024
…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>.
jieyouxu added a commit to jieyouxu/rust that referenced this issue Feb 20, 2024
…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>.
jieyouxu added a commit to jieyouxu/rust that referenced this issue Feb 20, 2024
…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>.
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Feb 21, 2024
…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.
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Feb 21, 2024
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.
bors added a commit to rust-lang/cargo that referenced this issue Jun 19, 2024
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-cleanup Category: PRs that clean code up or issues documenting cleanup. D-diagnostic-infra Diagnostics: Issues that affect all diagnostics, or relate to the diagnostic machinery itself. E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-diagnostics Working group: Diagnostics
Projects
None yet
Development

No branches or pull requests

16 participants