When a method call includes a named argument such as:
f(named: x)
the analyzer represents the AST node `named: x` as an Expression. But
it's not safe for the migration tool to try to modify it in the same
way that it modifies other expressions (e.g. by surrounding it with
`(...) as Type`), because that would produce a parse error.
Improves the analyzer behavior for #45583. Note however that this is
not a complete fix yet; see the test case included in this CL for why.
Bug: https://github.com/dart-lang/sdk/issues/45583
Change-Id: I79ffcdd22654221ff7e0784b3355ecfd2ec0f01c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194008
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
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>
This partially addresses #44675 by ensuring that if an extension's
extended type is nullable, we won't try to insert an unnecessary null
check at the call site.
It's not a complete fix, though, because we still don't account for
obvious indications of non-null intent when analyzing the extension
definition itself. I plan to address that in future CLs.
Bug: https://github.com/dart-lang/sdk/issues/44675
Change-Id: Ia0ca37b89470a2d4882ae32cd842552ffd34930e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180263
Reviewed-by: Samuel Rawlins <srawlins@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>
The packages 'analyzer' and 'nnbd_migration' tightly depend on each
other via MigrationResolutionHooks. I will publish analyzer 0.40.4
shortly after this CL lands.
Change-Id: I6f5e51f88e0020a1674ffb251712658e896170e7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164900
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
`FutureOr<T?>` is already nullable, because `FutureOr<T?>` ==
`FutureOr<T | Null>` == `Future<T | Null> | T | Null`. So the second
`?` is redundant.
Also fix a bug where we were treating a `Null` type as `Never` if we
couldn't find a reason to require it to be nullable; this was
inconsistent with how the FixBuilder was handling `Null` (which is to
leave it unchanged).
Change-Id: I5e0e4b7fb449988d8ff0dd87a2b4c3da4a6ecebb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/155506
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
When running the FixBuilder we need to override the behavior of
ClassElement.interfaces so that it returns the post-migration
interface types. We also need to override the behavior of
LibraryElement.isNonNullableByDefault so that the library being
migrated is considered opted in during FixBuilder analysis. This
required adding hooks to the analyzer to allow those overrides.
Additionally, since the analyzer caches the InheritanceManager, we
need to clear that cache out just prior to running the FixBuilder, so
that no pre-migration interface types leak into the FixBuilder.
Fixes#40475.
Fixes#42139.
Change-Id: I45e2c30ebe8c12e4a599e2458d77a3731bad0f98
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150260
Reviewed-by: Janice Collins <jcollins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@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, all parameters returned by
MigrationResolutionHooksImpl.getExecutableParameters were synthetic
ones that came from translating the executable element's decorated
function type to its final post-migration type. Synthetic parameters
can't be field formal parameters, so this meant that when the
FixBuilder was re-resolving a constructor containing field formal
parameters, references to the field in the constructor body would get
incorrectly interpreted as pointing to the parameter rather than the
field, and that would cause them to be subject to promotion.
The fix is to only return synthetic parameters from
MigrationResolutionHooksImpl.getExecutableParameters when the
executable in question is in a library *not* being migrated. When
it's in a library that *is* being migrated, we can just return all of
the executable's parameters without translating them, because we know
that the parameters themselves have been visited by the NodeBuilder
and EdgeBuilder, hence they all have thier own associated decorated
types.
Fixes#41405.
Change-Id: I5e3c411dc92fe242a959a5670b9253fbccea2b31
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144122
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This change adds the ability to detect when one branch of a
conditional expression is weak-only due to nullability. For example,
in the following code:
int f(int/*!*/ i) => i == null ? g(null) : i;
int g(int j) => ...;
the call to `g` can only happen in weak checking mode. The migrator
also now understands that j only needs to be nullable if i is
nullable.
Fixes#41555.
Partially addresses #41551.
Bug: https://github.com/dart-lang/sdk/issues/41555
Change-Id: I02c9c3072f0104db0f1a5b432fb1f2f8f06d5282
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144120
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
The new name more accurately reflects the purpose, and will make tests
less confusing when we add analysis of weak-only conditional
expressions in a follow-up CL.
Change-Id: I388f68173a1b41456690ebd0082e9c8d0ebe2e06
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144003
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
In the FixBuilder, when we are discarding one of the branches of an
if-statement or if-element due to it being weak-only, we only visit
the branch we're keeping; this avoids trying to make changes to code
that's being removed. However, if we are keeping both branches and
simply warning about one of them being weak-only, then we need to
visit both branches so that fixes are applied to both of them.
Change-Id: I26a1bfcb5f2ef4a111b1117637bce74e42d3db07
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144000
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Previously, if we migrated a code snippet like this:
void f(int/*?*/ i) {
/*late*/ int j = i/*!*/;
}
the migration result would leave the hints and add the hinted text,
resulting in:
void f(int?/*?*/ i) {
/*late*/ late int j = i!/*!*/;
}
We now simply remove the `/*` and `*/` surrounding each hint (with
whitespace corrections as needed), to produce:
void f(int? i) {
late int j = i!;
}
Change-Id: Ic4e1216cb4693d73d71c9ab1a37d5cc15b8ef334
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/143460
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
We now sort changes into the following categories (in order):
- Dead code removals
- Casts added
- Null checks added
- Required keywords added
- Types made nullable
- Casts now unnecessary
- Language version comments removed
The reasoning is that this corresponds roughly to how important it is
for a human to inspect each kind of change to make sure it is safe
(dead code removals, for example, are highly likely to indicate an
incorrect migration, where as dropping of unnecessary casts is benign
and almost certainly correct).
Change-Id: I49e3adac3ac98de1a9298f9caadb76c73a9c67d9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/142542
Reviewed-by: Janice Collins <jcollins@google.com>
Previously, null check hints were only affecting the behavior of
EdgeBuilder, which meant that we handled simple cases like:
int f(int/*?*/ x) => x/*!*/;
(because the EdgeBuilder wouldn't build an edge from the type of `x`
to the return type of `f`, so the return type of `f` would be
non-nullable int, and hence the FixBuilder would insert a `!` in order
to prevent an error).
But if there were some *other* reason for the destination type to be
nullable, then no `!` would get inserted.
This CL adds explicit logic to the FixBuilder to ensure that a `/*!*/`
hint causes a `!` to be inserted whether it's necessary or not.
Change-Id: I283bb9793d8d01408333f5b56562286bf19d8773
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/140904
Reviewed-by: 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>
This CL extends ElementTypeProvider so that it also tracks type
parameter bounds. This allows the FixBuilder to reason correctly
about the what generic function types will look like after migration,
and that in turn prevents it from trying to add unnecessary casts.
Change-Id: I3c909a3b5ba14bf9f2aad4e58d0981c1b7b5bdd2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134412
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Previously, each piece of analyzer code needed during the FixBuilder
stage of migration needed to use the ElementTypeProvider interface to
access element types (rather than reading the types from the elements
directly), in order to ensure that the proper post-migration types
were used when building fixes. This required a lot of plumbing, and
there was a high risk that we would accidentally fail to use the
indirection mechanism in a key location and access the elements
directly.
This CL changes the approach so that the ElementImpl classes
themselves are responsible for indirecting through
ElementTypeProvider. This means that we can back out all the
plumbing, and there is no risk of accidentally failing to use the
indirection mechanism.
Change-Id: Ic1a0acc37f0350fc13b487f73fc1ad5225d7a090
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134360
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This required adding logic to the `TypeParameterType` case of the
`toFinalType` function, so that we look up the decorated type of the
type parameter bound, and convert it to a final type as well.
(Previously we were just using the bound stored in the element model,
which was in many cases the bounds from prior to migration, and hence
it was a `*` type so it behaved as though it was non-nullable).
This required moving `toFinalType` into the `Variables` class so that
it could look up the decorated types of type parameter bounds.
Fixes#40355.
Change-Id: I53c2636aef6954500589e924b9755a297c2b30f9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134061
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Mike Fairhurst <mfairhurst@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>