Previously, if the migration tool encountered a Future expression with
a bad type (e.g. a Future<String?> where a Future<String> was needed),
it would "fix" the problem by introducing a cast. That is nearly
always the wrong thing to do; what we want to do is null check the
value that the future *completes* with.
This CL changes the migration tool so that it fixes this case by
appending `.then((value) => value!)` to the future expression.
Fixes#45472.
Bug: https://github.com/dart-lang/sdk/issues/45472
Change-Id: I7a35b54f673936e2e4b0f8f3a077ba8bf684b4eb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193700
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
With this change, the functionality of changing iterable method calls
such as `.firstWhere` into extension method calls such as
`.firstWhereOrNull` is working well enough that I think we can switch
it on.
We don't update the pubspec properly yet; that will be addressed in
follow-up CLs.
Change-Id: I758332e9752b12d399ecb5c70cf37bbc8da77d6c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/168988
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This logic allows us to migrate code like this:
int firstEven(Iterable<int> values)
=> values.firstWhere((i) => i.isEven, orElse: () => null);
Into:
import 'package:collection/collection.dart' show IterableExtension;
int? firstEven(Iterable<int> values)
=> values.firstWhereOrNull((i) => i.isEven);
Rather than the default behavior, which would migrate it to:
int? firsteven(Iterable<int?> values)
=> values.firstWhere((i) => i!.isEven, orElse: () => null);
(The new migration is far superior because it doesn't require the
input iterable to accept null).
This functionality is disabled at the moment, because:
- The changes it makes don't show up properly in the web preview.
- The pubspec is not yet properly updated.
I will address these issues in follow-up CLs and then enable the
feature.
Change-Id: I96f3b12d682c586631920b38406bad6aa3f4789e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/168500
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
We produce a diagnostic under the following conditions:
- For compound assignments, if the type read from the LHS is nullable
(this is illegal after NNBD, and requires user intervention to fix).
- For compound assignments, if the type returned from the combiner is
not assignable to the LHS (this was required prior to NNBD, but it
is a stricter condition after migration, both because the LHS might
have a non-nullable type, and because implicit downcasts are not
allowed).
- For null-aware assignments, if the type read from the LHS is
non-nullable (this indicates that once strong mode is enabled, the
assignment will be dead code).
Bug: https://github.com/dart-lang/sdk/issues/38676
Change-Id: Icb242ba36437e38364ada069880831eb05e3a513
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/145664
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Previously we considered such code "dead", but it's not really dead
until Dart 3.0. Removing it changes weak mode semantics, and we don't
want to do that automatically.
Fixes#41231
Change-Id: I6a88f016b4f3fcba160dcb738558867219715cbb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/143941
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Usually when migration removes dead code it can just delete it
(e.g. because it's removing a statement from a block and blocks can
contain any number of statements). However, in certain contexts dead
code must be replaced with something else, such as an empty statement.
Change-Id: I65faf017897215d750395dc78a2bf8a157d8dce0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136441
Reviewed-by: Janice Collins <jcollins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
The target of a property access can be null if it is a cascade. If that
access is then modified, fix aggregator gets a NPE. This issue is not
true for index & method calls, same fix applies.
Change-Id: I98eac3c8f7eba5300b30ff366ee227430726440c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135963
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
There were two problems:
1. An info object isn't being properly passed to
NodeChangeForExpression, causing an assertion failure during the
FixAggregator stage. I've modified the assertion check so that it
happens earlier, during the FixBuilder, so that the exception is
caught by permissive mode logic and doesn't cause a hard crash.
I'll work on fixing the assertion in a follow-up CL.
2. The hack described in https://github.com/dart-lang/sdk/issues/40536
was causing a bogus assertion failure in
_PassThroughBuilderImpl._checkParenLogic. I've weakened the
assertion slightly so that for now, the hack won't cause trouble.
See the issue for more information about what's happening--this
should be cleaned up eventually but it's probably not high
priority.
Bug: https://github.com/dart-lang/sdk/issues/40533
Change-Id: Iee832fbd07e54b02d2c52f79a097ff9d973cda9a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134980
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Previously, FixAggregator provided one NodeChange-derived class for
each possible kind of fix; if multiple fixes had to be made to a
single AST node, its client (FixBuilder) was required to nest them in
the appropriate order. FixBuilder accomplished this by carefully
controlling the order in which it decided on fixes. Unfortunately,
this careful control of order is becoming burdensome for FixBuilder to
maintain.
With this change, the class hierarchy of NodeChange mirrors the class
hierarchy of AstNode, with different fields for each possible kind of
fix. This means that FixBuilder can decide on fixes in whatever order
is convenient.
Change-Id: I1c79cfc37b6289051b3927c67867b77a1dbb5ebf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/133541
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Now there are only two circumstances in which an AtomicEdit won't have
information about the reason for the change:
- The AtomicEdit object was associated with adding or removing parens,
in which case we don't compute information about the reason for the
change because it's not straightforward to compute, and it should be
clear to the user anyway.
- Information about the reason for the change hasn't been plumbed
through properly (this should be addressed in follow-up CLs).
Since we are rapidly approaching a situation where nearly all
AtomicEdits will have reason information, the separation between
AtomicEdit and AtomicEditWithInfo now seems silly, so I just moved the
`info` object into AtomicEdit and removed AtomicEditWithInfo entirely.
Change-Id: I61afd9cd58b71d4695685e65d0142a2693b8e6b2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/132451
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>
FixAggregator will form the last stage of the migration engine, taking
the output of FixBuilder (which is a map from AST nodes to individual
changes) and feeding it into the EditPlan infrastructure to produce
the complete set of edits for any given file.
In a follow-up CL, I will modify FixBuilder to make use of the
NodeChange classes defined here.
Change-Id: I73fd67d7e989194049d91f978a7d9134814d3265
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129304
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>