|
1 | 1 | # Null Safety Migration Tooling |
2 | 2 |
|
3 | | -Note: the null safety migration tooling and workflow is in an early state; |
4 | | -this doc will be updated as the steps and workflow are simplified. |
| 3 | +Note: the null safety migration tooling is in an early state and may have bugs |
| 4 | +and other issues. |
5 | 5 |
|
6 | | -## Building the NNBD sdk |
| 6 | +For best results, use SDK version 2.9.0-10.0.dev or higher. |
7 | 7 |
|
8 | | -In order to run the tool currently you have to be able to build your own copy |
9 | | -of the Dart SDK. |
| 8 | +## How Migration Works |
10 | 9 |
|
11 | | -To do this, run: |
| 10 | +The migration uses a _new interactive algorithm_ designed specifically for Dart |
| 11 | +null safety. |
12 | 12 |
|
13 | | -``` |
14 | | -./tools/build.py -mrelease --nnbd create_sdk |
| 13 | +Typical code migration tools are designed to be run once, handle most cases, and |
| 14 | +let the developer do manual cleanup on the result. This does **not work well** |
| 15 | +for Null-Safety and attempting this workflow will result in a lot more manual |
| 16 | +work. Similarly, after your migration has been applied, the migration **cannot |
| 17 | +be rerun** without first reverting it. |
| 18 | + |
| 19 | +### Why does the interactive approach save so much time? |
| 20 | + |
| 21 | +Remember that Dart already has nullable types. Every type in old Dart code is |
| 22 | +nullable! What old Dart lacks is _non-null_ types. |
| 23 | + |
| 24 | +And like most migrations, our tool tries to preserve your code's current |
| 25 | +behavior. In the case of null safety, we may mark a lot of your types as |
| 26 | +nullable -- because they really were nullable before. |
| 27 | + |
| 28 | +Nulls are traced through your program as far as they can go, and types are |
| 29 | +marked nullable in this process. If the tool makes a single mistake or choice |
| 30 | +you disagree with, it can lead many excess nullable types. |
| 31 | + |
| 32 | +### Interactive feedback to the tool |
| 33 | + |
| 34 | +Unintentional null is the top cause of crashes in Dart programs. By marking your |
| 35 | +intention with comments like `/*?*/` and `/*!*/`, we can stop these |
| 36 | +unintentional nulls from spreading through your program in your migrated code. |
| 37 | +Adding a small number of these hints will have a huge impact on migration |
| 38 | +quality. |
| 39 | + |
| 40 | +The high level workflow of the tool is therefore driven through an interactive |
| 41 | +web UI. After running `dart migrate`, open the resulting url in a browser. Scan |
| 42 | +through the changes, use the "nullability trace" feature to find the best place |
| 43 | +to add a nullability hint (adding a hint in the best place can prevent dozens of |
| 44 | +types from being made nullable). Rerun the migration and repeat, committing the |
| 45 | +hints as you go. When the output is correct and acceptable, apply the migration |
| 46 | +and publish your null safe code! |
| 47 | + |
| 48 | +For example, |
| 49 | + |
| 50 | +```dart |
| 51 | +List<int> ints = const [0, null]; |
| 52 | +int zero = ints[0]; |
| 53 | +int one = zero + 1; |
| 54 | +List<int> zeroOne = [zero, one]; |
15 | 55 | ``` |
16 | 56 |
|
17 | | -The NNBD sdk now lives under the ReleaseX64NNBD sub-directory of your build |
18 | | -directory, e.g. |
| 57 | +The default migration will be backwards compatible, but not ideal. |
19 | 58 |
|
| 59 | +```dart |
| 60 | +List<int?> ints = const [0, null]; |
| 61 | +int? zero = ints[0]; |
| 62 | +int one = zero! + 1; |
| 63 | +List<int?> zeroOne = <int?>[zero, one]; |
20 | 64 | ``` |
21 | | -xcodebuild/ReleaseX64NNBD/dart-sdk/ |
| 65 | + |
| 66 | +`zero` should not be marked nullable, but it is. We then have cascading quality |
| 67 | +issues, such as null-checking a value that shouldn't have been marked null, and |
| 68 | +marking other variables as null due to deep null tracing. We can fix this all by |
| 69 | +adding a single `/*!*/` hint. |
| 70 | + |
| 71 | +```dart |
| 72 | +List<int?> ints = const [0, null]; |
| 73 | +int/*?*/ zero = ints[0]!; // Just add /*?*/ here, the migration tool does the rest! |
| 74 | +int one = zero + 1; |
| 75 | +List<int> zeroOne = <int>[zero, one]; |
22 | 76 | ``` |
23 | 77 |
|
| 78 | +If you add one hint before migrating, you have done the equivalent of making |
| 79 | +five manual edits after migrating. To find the best place to put your hints, use |
| 80 | +the preview tool's nullability trace feature. This lets you trace back up to the |
| 81 | +root cause of any type's inferred nullability. Add hints as close to the |
| 82 | +original source of null as possible to have the biggest impact to the migration. |
| 83 | + |
| 84 | +**Note**: The migration tool **cannot be rerun on a migrated codebase.** At |
| 85 | +that point in time, every nullable and non-nullable type is indistinguishable |
| 86 | +from an **intentionally** nullable or non-nullable type. The opportunity to |
| 87 | +change large numbers of types for you at once without also accidentally changing |
| 88 | +your intent has been lost. A long migration effort (such as one on a large |
| 89 | +project) can be done incrementally, by committing these hints over time. |
| 90 | + |
24 | 91 | ## Migrating a package |
25 | 92 |
|
26 | | -- build a NNBD version of the SDK (see above) |
27 | 93 | - select a package to work on |
28 | | -- in that package, edit the `analysis_options.yaml` to enable the NNBD |
29 | | - experiment from the POV of the analyzer: |
30 | | -```yaml |
31 | | -analyzer: |
32 | | - enable-experiment: |
33 | | - - non-nullable |
34 | | -``` |
35 | | -- run `pub get` for the package (and, verify that the |
36 | | - `.dart_tool/package_config.json` file was created) |
| 94 | +- run `pub get` for the package |
37 | 95 |
|
38 | 96 | Then, run the migration tool from the top-level of the package directory: |
39 | 97 |
|
40 | 98 | ``` |
41 | | -<sdk-repo>/xcodebuild/ReleaseX64NNBD/dart migrate . |
| 99 | +dart migrate |
42 | 100 | ``` |
43 | 101 |
|
44 | | -The migration tool will run, print the proposed changes to the console, and |
45 | | -display a url for the preview tool. Open that url from a browser to see a rich |
46 | | -preview of the proposed null safety changes. |
| 102 | +The migration tool will run and display a url for the web UI. Open that url from |
| 103 | +a browser to view, analyze, and improve the proposed null-safe migration. |
47 | 104 |
|
48 | 105 | ## Using the tool |
49 | 106 |
|
50 | 107 | 1. Run the tool (see above). |
51 | | -2. Once analysis and migration suggestions are complete, open the indicated url |
52 | | -in a browser. |
53 | | -3. Start with an important or interesting file in your package on the left |
54 | | -side by clicking on it. |
| 108 | +2. Once analysis and migration is complete, open the indicated url in a browser. |
| 109 | +3. Start with an important or interesting file in your package on the left side |
| 110 | + by clicking on it. |
55 | 111 | 4. Look at the proposed edits in the upper right, and click on them in turn. |
56 | 112 | 5. If you see an edit that looks wrong: |
57 | 113 | 1. Use the "trace view" in the bottom right to find the root cause |
58 | | - 2. Go to your editor and make a change to the original file by adding a hint |
59 | | - (`String foo` ==> `String/*!*/ foo`) or making other changes as needed. |
60 | | - 3. You can have the migration tool perform this itself, although right now |
61 | | - for large packages this takes a prohibitively long time. |
62 | | - 1. ***Warning: DO NOT mix edits in your editor and edits applied by the |
63 | | - migration tool. We have not yet written the necessary logic to |
64 | | - prevent the migration tool from clobbering files edited while the |
65 | | - preview tool is running.*** |
66 | | - 2. To try this, from the 'Edit Details' area in the bottom right select |
67 | | - the `Force type to be non-nullable` or `Force type to be nullable` |
68 | | - links. These will add the indicated hints on disk and recompute the |
69 | | - migration suggestions. |
70 | | -6. After some edits are complete, control-C the migration and rerun it. If |
71 | | -some things are still wrong, return to step 5. |
72 | | -7. Once all edits are complete and you've rerun migration and are satisfied with |
73 | | -the output: |
| 114 | + 2. Either click on an "add hint" button to correct it at the root, or open |
| 115 | + your editor and make the change manually. |
| 116 | + 1. Some changes are as simple as adding a `/*!*/` hint on a type. The |
| 117 | + tool has buttons to do this for you. |
| 118 | + 2. Others may require larger refactors. These changes can be made in |
| 119 | + your editor, and may often be committed and published immediately. |
| 120 | + 3. Periodically rerun the migration and repeat. |
| 121 | +6. Once you are satisfied with the proposed migration: |
74 | 122 | 1. Save your work using git or other means. Applying the migration will |
75 | | - overwrite the existing files on disk. |
76 | | - 2. Rerun the migration with `--apply-changes`, or click the |
77 | | - `Apply Migration` button in the interface. |
78 | | -8. Remove any SDK constraint in your pubspec.yaml. |
79 | | -9. Remove any opt-out comments in your library files (e.g.: `// @dart = 2.6`). |
80 | | -10. Rerun `pub get` and test your package. |
| 123 | + overwrite the existing files on disk. |
| 124 | + 2. Rerun the migration by clicking the `Apply Migration` button in the |
| 125 | + interface. |
| 126 | + 3. Tip: leaving the web UI open may help you if you later have test failures |
| 127 | + or analysis errors. |
| 128 | +7. Rerun `pub get` and test your package. |
| 129 | + 1. If a test fails, you may still use the preview to help you figure out |
| 130 | + what went wrong. |
| 131 | + 2. If large changes are required, revert the migration, and go back to step |
| 132 | + one. |
| 133 | +8. Commit and/or publish your migrated null-safe code |
81 | 134 |
|
82 | 135 | ## Providing feedback |
83 | 136 |
|
84 | 137 | Please file issues at https://github.com/dart-lang/sdk/issues, and reference the |
85 | | -`analyzer-nnbd-migration` label (you may not be able to apply the label yourself). |
| 138 | +`analyzer-nnbd-migration` label (you may not be able to apply the label yourself). |
0 commit comments