Previously, we used the offset of an AST node as a unique ID to
represent it, with a special hack to make sure we can distinguish a
generic function type from its return type (which has the same
offset). But in an upcoming CL, I will need to start associating
unique IDs with expressions, so that will bring up a whole bunch more
ambiguity.
This CL switches to a technique where we combine the offset and the
end of any source span into a single unique identifier. The algorithm
produces an output that is no larger than the square of either input.
Since we have 64-bit integers in Dart, this should be adequate for any
reasonable input file size.
Change-Id: I68ce27533eb485cc824e1080326d02de98bff1e7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/132404
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
The new FixBuilder component of the migration engine communicates the
changes it produces using a "changes" data structure of type Map<int,
List<AtomicEdit>>, and is not compatible with the old
SingleNullabilityFix class (which made the incorrect assumption that
each SourceEdit was associated with a single fix). This CL changes
the migration engine's instrumentation API to use this data structure,
and changes its listener API to avoid reference to the
SingleNullabilityFix class. This allows SingleNullabilityFix to be
deleted.
This required changing AtomicEditWithReason so that it can hold
multiple fix reasons, and a NullabilityFixDescription. It does so
using a new object, AtomicEditInfo, and AtomicEditWithReason is
renamed to AtomicEditWithInfo.
In addition, the old FixInfo class is removed, since it carries the
same information as the new AtomicEditWithInfo class.
This required deleting the "incremental workflow" functionality from
UnitRenderer. This functionality was disabled, and I believe it
wouldn't have worked anyway (since it, too, made the incorrect
assumption that each SourceEdit was associated with a single fix).
Change-Id: Id965cfd803b408c66f15436017b87fc3e46521c6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/132306
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
The FixBuilder implemenation is now at feature parity with the
non-FixBuilder implementation when testing with api_test.dart, except
for one test of extension functionality (which doesn't seem worth
fixing right now, since extensions are still so rarely used).
Change-Id: I89df7ec388824fa9f4181706bfadbaeb232d2b5f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/132169
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Previously, we tried to hook in to the resolver so that every time it
tried to check the type of a TypeAnnotation, the FixBuilder would see
if it needed to add a `?` to it before letting the resolver continue.
This was clumsy and didn't catch all the TypeAnnotations. This CL
changes to a simpler technique where we simply visit all
TypeAnnotations, making note of where we need to add `?`s and updating
types, prior to re-running resolution.
Change-Id: Idf5ba286415fb2b90e2cd1d5ba3635b6b559aef6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/132164
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
We will need to hook into this for migration as well, because the type
arguments of typed literals will be changed by migration from star
types to either nullable or non-nullable types.
Change-Id: Ie721390effd9cf733b9f977c1ba729f6fc4b2ebb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131920
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This should make the fix builder faster, since TypedLiteralResolver
has to query the elements of various collections multiple times.
Change-Id: If49317b8e095b49b693e8a4885914a631efd6d2e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131844
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
One minor issue remains: when dead code elimination causes a
collection to become empty, migration should insert explicit type
parameter(s) to ensure that the empty collection will still get
analyzed as having the right type. This is noted in TODO comments.
Change-Id: Ibed25f6cb8ed9eec3ddccb0444178960c6b35c84
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131843
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Looks like we used to do this, but we stopped when we started changing
this pattern:
```
dynamic x = 1;
int y = x;
```
Our first iteration treated `dynamic` as nullable all the time. This is
not ideal in the example above. So we went back and instead began
treating `dynamic` as nullable only when it was observed to have a null
value.
However, in the case of unbounded type parameters:
```
class C<T> {}
```
We don't want to wait to observe:
```
C<Object> o = C(null);
```
before treating the bound as nullable.
Change-Id: I50b8ca6188d82cd62f795ada99a355afe2eae771
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131714
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Instead of storing planIndex in the class (which makes it hard to
follow what methods are expected to update it, and consequently hard
to tell that _processPlans will process each plan exactly once), pass
it between _processPlans and _handleRemoveEditPlans so that it's
clearer how it is updated.
Change-Id: I1b03ad2b8bb0ae5e488d02827d2a4a56f37ede21
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131711
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Consider a generic AST structure like this:
A
/ \
B C
/ \
D E
Previously, if we wanted to build a "pass through" edit plan for A,
incorporating inner plans representing changes to be made to B, D, and
F, we would do the following: (1) pass each inner plan through the
_incorporateParent function, which might leave it unchanged or might
produce a plan targeted at the parent node, and then (2) ask each
inner plan for its set of changes, and produce a new plan for A
containing those changes.
The purpose of step (1) was to allow an edit plan to describe changes
that might affect the node above it. For example, changing an
expression might cause the parentheses surrounding it to become
unnecessary, so those parentheses would be removed (that's the case
that's currently implemented). Or, removing a list element from a
list might necessitate adjusting the comma before or after it, which
belongs to the parent node (this isn't implemented yet).
This had a design flaw: step (1) might produce overlapping changes.
For instance, calling _incorporateParent on the plans for D and E
might conceivably both plans for C, and those plans might try to make
overlapping (and thus incompatible) changes to node C.
It turns out that this isn't a problem for eliminating unnecessary
parens, because it's impossible for two edit plans to target two
different non-overlapping AST nodes whose parents are both the same
ParenthesizedExpression node (because a ParenthesizedExpression has
only one child). But it will be a problem for removing elements
lists, statements from blocks, declarations from classes, etc.
The new strategy is that we will bubble inner plans up the tree one
level at a time. In the example above, we would first combine the
inner plans for D and E into a pass through plan for C. Then we would
combine the plans for B and C into a plan for A. This is a bit more
work, but I believe it can be made efficient in typical use cases, and
it will avoid the problem described above because at each stage, all
the inner plans we combine will have the same parent, so it will be
tractable to figure out their interactions with each other.
Some vestiges of the old _incorporateParent approach still remain; I
plan to remove them in a follow-up CL.
Change-Id: I9ecbdb1549b949177ffc5d66447d4aede77e2dd9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131400
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
The derived class, NodeProducingEditPlan, represents an edit plan
whose effect will be to transform the source AST node into another AST
node (e.g. replace an expression with one of its subexpressions). The
base class, EditPlan, will be able to represent more general kinds of
edit plans.
Future CLs will introduce edit plans that don't result in an AST node,
e.g. an edit plan that removes a source statement or a collection
element, or an edit plan that replaces one source statement or
collection element with several.
Change-Id: I01455f643b858c6500d864be679b12768029cffb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/130941
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
In later CL's, I will make it possible for dead code to be deleted
instead of commented out. This change prepares for that, by
explicitly noting which API tests assume that dead code is commented
out.
Change-Id: I4e0ff6f1de4c0ab5fda050956944725b2d23dc5e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/130931
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
In upcoming CLs, I'm going to need access to the EditPlanner when
finalizing edit plans, and I'd rather do so by making finalize a
method on EditPlanner. Doing so now to make future code reviews
simpler.
Change-Id: I6182e2224b548b2919f0c8f454f558b72c17e5d5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/130926
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This factory class serves three purposes:
- In a future CL, it will contain fields to customize the global
behavior of the edit plans that are created (e.g. whether code
should be removed or commented out).
- In a future CL, it will contain a pointer to the original (text)
contents of the source file. This will allow edit plans to make
more sophisticated changes such as adjusting indentation.
- In a future CL, it will contain methods for creating common kinds of
"extract" edit plans such as null-checking an expression or making a
type nullable. (Currently the client must create these edit plans
using EditPlanner.extract, which has an error-prone API).
Change-Id: I87ac75b73003d0e7b416e3d2121eadb2770da5ed
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129802
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
We were getting a static error in package:logging for
```dart
Zone recordingZone;
// ...
runZoned(() {
recordingZone = Zone.current;
// ...
});
// ...
expect(records.first.zone, equals(recordingZone));
```
That is a static error if we don't make `recordingZone` late or
nullable.
Change-Id: Iab5e47dd8df375c7dc5283ce4570ad1321ba919e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/130738
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Turns out the flow analysis bug was actually an issue with calling
toString() on an enum. This happened to throw the same type of exception
as caused by the shared part file, so it blended in. Flow analysis was
not itself broken.
Tested the other methods on enum too, but no others seemed to have this
problem.
Change-Id: I5e9e1be3f02adf0177c276c63d0c4f4fa2cb94db
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/130622
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>