2020-06-11 03:33:25 +00:00
|
|
|
# Null safety migration tooling
|
2019-06-09 17:45:35 +00:00
|
|
|
|
2020-06-03 16:06:53 +00:00
|
|
|
**Note**:
|
2020-03-19 23:12:24 +00:00
|
|
|
|
2020-10-13 16:29:08 +00:00
|
|
|
* This migration tool is now available through the SDK, using the `dart
|
|
|
|
migrate` command. Support for running it via `pub activate` is deprecated.
|
2020-06-03 16:06:53 +00:00
|
|
|
* The null safety migration tooling is in an early state and may have bugs and
|
|
|
|
other issues.
|
|
|
|
* As null safety is still in preview, we recommend only doing trial
|
2020-06-11 03:33:25 +00:00
|
|
|
migrations. The final migration of apps and packages should not be done
|
2020-06-03 16:06:53 +00:00
|
|
|
until the feature is more complete.
|
|
|
|
* For best results, use SDK version 2.9.0-10.0.dev or higher.
|
2020-03-19 23:12:24 +00:00
|
|
|
|
2020-06-11 03:33:25 +00:00
|
|
|
## How migration works
|
2020-03-19 23:12:24 +00:00
|
|
|
|
2020-06-11 03:33:25 +00:00
|
|
|
The migration uses a _new interactive algorithm_ designed specifically for [Dart
|
|
|
|
null safety](https://dart.dev/null-safety).
|
2020-03-19 23:12:24 +00:00
|
|
|
|
2020-05-21 19:50:23 +00:00
|
|
|
Typical code migration tools are designed to be run once, handle most cases, and
|
|
|
|
let the developer do manual cleanup on the result. This does **not work well**
|
2020-05-30 23:40:42 +00:00
|
|
|
for null safety and attempting this workflow will result in a lot more manual
|
2020-05-21 19:50:23 +00:00
|
|
|
work. Similarly, after your migration has been applied, the migration **cannot
|
|
|
|
be rerun** without first reverting it.
|
|
|
|
|
|
|
|
### Why does the interactive approach save so much time?
|
|
|
|
|
|
|
|
Remember that Dart already has nullable types. Every type in old Dart code is
|
|
|
|
nullable! What old Dart lacks is _non-null_ types.
|
|
|
|
|
|
|
|
And like most migrations, our tool tries to preserve your code's current
|
|
|
|
behavior. In the case of null safety, we may mark a lot of your types as
|
|
|
|
nullable -- because they really were nullable before.
|
|
|
|
|
|
|
|
Nulls are traced through your program as far as they can go, and types are
|
|
|
|
marked nullable in this process. If the tool makes a single mistake or choice
|
2020-06-03 16:06:01 +00:00
|
|
|
you disagree with, it can lead to many excess nullable types.
|
2020-05-21 19:50:23 +00:00
|
|
|
|
|
|
|
### Interactive feedback to the tool
|
|
|
|
|
|
|
|
Unintentional null is the top cause of crashes in Dart programs. By marking your
|
|
|
|
intention with comments like `/*?*/` and `/*!*/`, we can stop these
|
|
|
|
unintentional nulls from spreading through your program in your migrated code.
|
|
|
|
Adding a small number of these hints will have a huge impact on migration
|
|
|
|
quality.
|
|
|
|
|
|
|
|
The high level workflow of the tool is therefore driven through an interactive
|
2020-06-11 03:33:25 +00:00
|
|
|
web UI. After starting the tool with `dart migrate`, open the presented URL in a
|
|
|
|
browser. Scan through the changes, use the "nullability trace" feature to find
|
|
|
|
the best place to add a nullability hint (adding a hint in the best place can
|
|
|
|
prevent dozens of types from being made nullable). Rerun the migration and
|
|
|
|
repeat, committing the hints as you go. When the output is correct and
|
|
|
|
acceptable, apply the migration.
|
2020-05-21 19:50:23 +00:00
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
```dart
|
|
|
|
List<int> ints = const [0, null];
|
|
|
|
int zero = ints[0];
|
|
|
|
int one = zero + 1;
|
|
|
|
List<int> zeroOne = [zero, one];
|
2020-03-19 23:12:24 +00:00
|
|
|
```
|
|
|
|
|
2020-05-21 19:50:23 +00:00
|
|
|
The default migration will be backwards compatible, but not ideal.
|
2020-03-19 23:12:24 +00:00
|
|
|
|
2020-05-21 19:50:23 +00:00
|
|
|
```dart
|
|
|
|
List<int?> ints = const [0, null];
|
|
|
|
int? zero = ints[0];
|
|
|
|
int one = zero! + 1;
|
|
|
|
List<int?> zeroOne = <int?>[zero, one];
|
2020-03-19 23:12:24 +00:00
|
|
|
```
|
2020-05-21 19:50:23 +00:00
|
|
|
|
|
|
|
`zero` should not be marked nullable, but it is. We then have cascading quality
|
|
|
|
issues, such as null-checking a value that shouldn't have been marked null, and
|
|
|
|
marking other variables as null due to deep null tracing. We can fix this all by
|
|
|
|
adding a single `/*!*/` hint.
|
|
|
|
|
|
|
|
```dart
|
|
|
|
List<int?> ints = const [0, null];
|
2020-06-03 16:06:01 +00:00
|
|
|
int/*!*/ zero = ints[0]!; // Just add /*!*/ here, the migration tool does the rest!
|
2020-05-21 19:50:23 +00:00
|
|
|
int one = zero + 1;
|
|
|
|
List<int> zeroOne = <int>[zero, one];
|
2020-03-19 23:12:24 +00:00
|
|
|
```
|
|
|
|
|
2020-05-21 19:50:23 +00:00
|
|
|
If you add one hint before migrating, you have done the equivalent of making
|
|
|
|
five manual edits after migrating. To find the best place to put your hints, use
|
|
|
|
the preview tool's nullability trace feature. This lets you trace back up to the
|
|
|
|
root cause of any type's inferred nullability. Add hints as close to the
|
|
|
|
original source of null as possible to have the biggest impact to the migration.
|
|
|
|
|
|
|
|
**Note**: The migration tool **cannot be rerun on a migrated codebase.** At
|
|
|
|
that point in time, every nullable and non-nullable type is indistinguishable
|
|
|
|
from an **intentionally** nullable or non-nullable type. The opportunity to
|
|
|
|
change large numbers of types for you at once without also accidentally changing
|
|
|
|
your intent has been lost. A long migration effort (such as one on a large
|
|
|
|
project) can be done incrementally, by committing these hints over time.
|
|
|
|
|
2020-06-11 03:33:25 +00:00
|
|
|
<!-- TODO(srawlins): We should explain (or point to explanation of) "migrated"
|
|
|
|
code. I don't see any documents pointing out how null safety is enabled via
|
|
|
|
pubspec.yaml, or library-by-library comments. -->
|
|
|
|
|
2020-03-19 23:12:24 +00:00
|
|
|
## Migrating a package
|
|
|
|
|
2020-05-30 23:40:42 +00:00
|
|
|
1. Select a package to work on, and open a command terminal in the top-level of
|
|
|
|
the package directory.
|
|
|
|
2. Run `pub get` in order to make available all dependencies of the package.
|
2020-06-11 03:33:25 +00:00
|
|
|
3. It is best to migrate a package to null safety _after_ the package's
|
|
|
|
dependencies have migrated to null safety. Run
|
|
|
|
`pub outdated --mode=null-safety` to learn the migration status of the
|
|
|
|
package's dependencies. See the
|
|
|
|
[pub outdated documentation](https://dart.dev/tools/pub/cmd/pub-outdated)
|
|
|
|
for more information.
|
|
|
|
4. It is best to migrate a package starting from a clean code repository state
|
|
|
|
(`git status`, for example), in case you must revert the migration. Ensure
|
|
|
|
there are no pending changes in the package's code repository.
|
|
|
|
5. Run the migration tool from the top-level of the package directory:
|
2020-03-19 23:12:24 +00:00
|
|
|
|
2020-05-30 23:40:42 +00:00
|
|
|
```
|
|
|
|
dart migrate
|
|
|
|
```
|
2020-03-19 23:12:24 +00:00
|
|
|
|
2020-05-30 23:40:42 +00:00
|
|
|
The migration tool will display a URL for the web interface. Open that URL in a
|
|
|
|
browser to view, analyze, and improve the proposed null-safe migration.
|
2020-03-19 23:12:24 +00:00
|
|
|
|
|
|
|
## Using the tool
|
|
|
|
|
2020-03-20 23:08:36 +00:00
|
|
|
1. Run the tool (see above).
|
2020-05-30 23:40:42 +00:00
|
|
|
2. Once analysis and migration is complete, open the indicated URL in a browser.
|
2020-05-21 19:50:23 +00:00
|
|
|
3. Start with an important or interesting file in your package on the left side
|
|
|
|
by clicking on it.
|
2020-03-20 23:08:36 +00:00
|
|
|
4. Look at the proposed edits in the upper right, and click on them in turn.
|
|
|
|
5. If you see an edit that looks wrong:
|
|
|
|
1. Use the "trace view" in the bottom right to find the root cause
|
2020-05-21 19:50:23 +00:00
|
|
|
2. Either click on an "add hint" button to correct it at the root, or open
|
|
|
|
your editor and make the change manually.
|
2020-06-11 03:33:25 +00:00
|
|
|
* Some changes are as simple as adding a `/*!*/` hint on a type. The
|
|
|
|
tool has buttons to do this for you.
|
|
|
|
* Others may require larger refactors. These changes can be made in
|
|
|
|
your editor.
|
|
|
|
* Changes may even be committed to source code management before finally
|
|
|
|
_applying_ the migration. In this way, a migration of a large package
|
|
|
|
can be carried out over multiple sessions, or between multiple
|
|
|
|
engineers. Committing hints and other adjustments along the way helps
|
|
|
|
to separate the concerns of describing user intent vs committing to the
|
|
|
|
migration result.
|
2020-05-21 19:50:23 +00:00
|
|
|
3. Periodically rerun the migration and repeat.
|
|
|
|
6. Once you are satisfied with the proposed migration:
|
2020-03-20 23:08:36 +00:00
|
|
|
1. Save your work using git or other means. Applying the migration will
|
2020-05-21 19:50:23 +00:00
|
|
|
overwrite the existing files on disk.
|
2020-05-30 23:40:42 +00:00
|
|
|
* Note: In addition to making edits to the Dart source code in
|
|
|
|
the package, applying the migration edits the package's `pubspec.yaml`
|
|
|
|
file, in order to change the Dart SDK version constraints, under the
|
2020-06-02 15:51:11 +00:00
|
|
|
`environment` field, and the "Package Config" file, located in the
|
|
|
|
package's `.dart_tool` directory, named `package_config.json`.
|
|
|
|
2. Apply the migration by clicking the `Apply Migration` button in the
|
2020-05-21 19:50:23 +00:00
|
|
|
interface.
|
|
|
|
3. Tip: leaving the web UI open may help you if you later have test failures
|
|
|
|
or analysis errors.
|
2020-06-11 03:33:25 +00:00
|
|
|
7. Rerun `pub get`, then analyze and test your package.
|
|
|
|
1. If there are new static analysis issues, or if a test fails, you may
|
|
|
|
still use the preview to help you figure out what went wrong.
|
2020-05-21 19:50:23 +00:00
|
|
|
2. If large changes are required, revert the migration, and go back to step
|
2020-06-11 03:33:25 +00:00
|
|
|
one. The tool does not provide any revert capability; this must be done
|
|
|
|
via source code management (for example, `git checkout`).
|
|
|
|
|
|
|
|
<!-- TODO(srawlins): direct the user to publish, only after null safety leaves
|
|
|
|
tech preview. See the big note at https://dart.dev/null-safety.
|
|
|
|
8. Commit and/or publish your migrated null-safe code. -->
|
2020-03-20 18:13:28 +00:00
|
|
|
|
|
|
|
## Providing feedback
|
|
|
|
|
|
|
|
Please file issues at https://github.com/dart-lang/sdk/issues, and reference the
|
2020-05-21 19:50:23 +00:00
|
|
|
`analyzer-nnbd-migration` label (you may not be able to apply the label yourself).
|