Skip to content

Is the UX around fixes confusing? #60660

@bwilkerson

Description

@bwilkerson

Normally the analysis server will provide one fix for every diagnostic reported on a given line of code. For example, given

void twoDifferentDiagnostics(A a) { '${a.m1()} ${a.m2()}'; // diagnostics here } class A {}

There are two diagnostics, one indicating that m1 is not a member of A and one indicating that m2 is not a member of A, and there will be two fixes, one to declare m1 and one to declare m2. (Actually, there are four today because it can either be created as a method in A or as an extension method, but the basic behavior is the same: every diagnostic as a fix.)

However, if there are multiple diagnostics of the same kind (and/or the same message, I don't know how the comparison is done), then there is code in the analysis server that deduplicates fix-all-in-file fixes so that we won't have multiple fix-all-in-file fixes presented that will all do the same thing. That's good, but it also deduplicates the single location fixes in at least some cases. For example, given the code

void twoSimilarDiagnostics(A a) { useTwoB(a, a); // diagnostics here } void useTwoB(B b1, B b2) {} class A {} class B {}

There are two diagnostics of the same kind with the same message. There is one "Add cast" fix and one "Add cast everywhere in file" fix. The fact that there's only one fix-all-in-file fix is good, but the behavior of the first fix depends on where the cursor is on the line. In some places it will add a cast to the first argument and in some cases it will add a cast to the second argument. Clicking inside the highlight range of one diagnostic causes the fix to apply to that diagnostic, but clicking outside either highlight range appears to add the cast on the closest diagnostic.

The problem is worse if there are multiple diagnostics of different kinds where all of them have the same fix. The "apply this fix everywhere" messages don't distinguish the kind of diagnostic being fixed, but will only fix one kind of diagnostic. For example, given the code

void f(A a) { a = useB(getA()); // diagnostics here a = useB(getA()); // diagnostics here } A getA() => A(); B useB(B b) => b; class A {} class B {}

There are four diagnostics, two on each line. Two indicating that the types of the arguments don't match the types of the parameters, and two indicating that the type of the right-hand side of the assignment doesn't match the type of the variable on the left. Clicking on either line will produce two fixes, one to add a cast and one to add a cast everywhere. The single location fixes behave as above, changing behavior depending on where on the line the cursor is located. The fix-in-file fixes do the same, they change their behavior depending on where the cursor is on the line. But it seems wrong to me that Add cast everywhere in file" won't add a cast everywhere despite the fact that its a fix offered for all of the diagnostics.

The question is: is this confusing for users (in practical use, not just in theory) and if so what should the UX be?

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2A bug or feature request we're likely to work onarea-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.devexp-serverIssues related to some aspect of the analysis servertype-questionA question about expected behavior or functionality

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions