-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate to null safety #183
Conversation
Windows tests are failing due to an issue cloning a git dependency |
bin/sass_migrator.dart
Outdated
@@ -12,6 +12,6 @@ import 'package:sass_migrator/src/runner.dart'; | |||
// We can't declare args as a List<String> or Iterable<String> beacause of | |||
// dart-lang/sdk#36627. | |||
main(Iterable args) { | |||
if (process.argv != null) args = process.argv.skip(2); | |||
if (process.argv != null) args = process.argv!.skip(2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (process.argv != null) args = process.argv!.skip(2); | |
var argv = process.argv; | |
if (argv != null) args = argv.skip(2); |
I think it's generally better to do this slightly awkward two-step check than to include runtime non-null assertions where possible. Even though it's more verbose, it helps preserve the signal that every !
indicates something weird going on and needs extra attention and care.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/src/migration_visitor.dart
Outdated
List<Patch> get patches => UnmodifiableListView(assertNotNull(_patches)); | ||
List<Patch>? _patches; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you do sometimes need to access _patches
directly to get the modifiable version of this list, I'd split this up even further to minimize the number of necessary non-null assertions:
List<Patch> get patches => UnmodifiableListView(assertNotNull(_patches)); | |
List<Patch>? _patches; | |
List<Patch> get patches => UnmodifiableListView(_patches); | |
List<Patch> get _patches => assertNotNull(__patches); | |
List<Patch>? __patches; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/src/migrator.dart
Outdated
@@ -36,7 +37,7 @@ import 'utils.dart'; | |||
/// | |||
/// Most migrators will want to create a subclass of [MigrationVisitor] and | |||
/// implement [migrateFile] with `MyMigrationVisitor(this, entrypoint).run()`. | |||
abstract class Migrator extends Command<Map<Uri, String>> { | |||
abstract class Migrator extends Command<Map<Uri, String >> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple places here where a space is added after String
in a generic type. Did you forget to reformat? Is this missing a formatter check in CI? Or is the formatter adding bogus spaces?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops. My editor auto-formats on save, so I didn't think to run it manually (and I guess the migrator doesn't format after running).
It looks like the formatter check got dropped from the CI for both this repo and Dart Sass during the switch to GitHub Actions. Added it to the CI config.
@@ -170,7 +170,7 @@ class _DivisionMigrationVisitor extends MigrationVisitor { | |||
_patchOperatorToComma(last); | |||
} | |||
_withContext(() { | |||
channels.contents[0].accept(this); | |||
channels!.contents[0].accept(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment referencing https://github.com/dart-lang/sdk/issues/45348 so it's clear why this non-null assertion is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/src/utils.dart
Outdated
@@ -17,6 +17,9 @@ import 'package:sass/src/ast/node.dart'; | |||
import 'io.dart'; | |||
import 'patch.dart'; | |||
|
|||
T assertNotNull<T>(T? object) => | |||
object ?? (throw AssertionError('Unexpected null value.')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Part of the value of writing our own non-null assertion function is that it can produce a more relevant error message that explains why the value shouldn't be null, but putting it in a utils file kind of undermines that. I'd write a separate method for each class that needs to use it whose error message can explain why the variable should be initialized (e.g. https://github.com/sass/dart-sass/blob/295a11696240eca4f76f542fdb277b4b01bb7db6/lib/src/visitor/async_evaluate.dart#L528-L536).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
All but one use of this was for a variable that's initialized in visitStylesheet
of a MigrationVisitor
, so I added a method there. _configuredVariables
is initialized when a dependency is visited, but since it's the only case like that I just threw the error inline.
lib/src/renamer.dart
Outdated
@@ -183,7 +190,7 @@ class Renamer<T> { | |||
buffer.writeCharCode(next); | |||
} | |||
} else { | |||
buffer.writeCharCode(char); | |||
buffer.writeCharCode(char!); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you make the if ({...}.contains(char))
check above more explicit you won't need this assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/src/util/bidirectional_map.dart
Outdated
return value; | ||
} | ||
|
||
/// Finds the keys associated with a given value. | ||
Iterable<K> keysForValue(V value) sync* { | ||
if (_keysForValue.containsKey(value)) yield* _keysForValue[value]; | ||
if (_keysForValue.containsKey(value)) yield* _keysForValue[value]!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intermediate variable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/src/utils.dart
Outdated
@@ -135,11 +138,11 @@ bool isWhitespace(int character) => | |||
/// `$` at the start of variable names. | |||
FileSpan nameSpan(SassNode node) { | |||
if (node is VariableDeclaration) { | |||
var start = node.namespace == null ? 1 : node.namespace.length + 2; | |||
var start = node.namespace == null ? 1 : node.namespace!.length + 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intermediate variable, also below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
test/utils.dart
Outdated
@@ -63,7 +65,7 @@ Future<void> _testHrx(File hrxFile, String migrator) async { | |||
|
|||
if (files.expectedLog != null) { | |||
expect(process.stdout, | |||
emitsInOrder(files.expectedLog.trimRight().split("\n"))); | |||
emitsInOrder(files.expectedLog!.trimRight().split("\n"))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intermediate variable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
tool/grind.dart
Outdated
@@ -43,7 +43,7 @@ sanityCheckBeforeRelease() { | |||
/// Returns the environment variable named [name], or throws an exception if it | |||
/// can't be found. | |||
String environment(String name) { | |||
var value = Platform.environment[name]; | |||
String? value = Platform.environment[name]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this type annotation is unnecessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (I added it because the migrator was getting confused without it, but I forgot to remove it afterwards)
lib/src/migrators/module.dart
Outdated
var tuple = importCache.canonicalize(Uri.parse(ruleUrl), | ||
baseImporter: importer, baseUrl: currentUrl, forImport: true); | ||
var canonicalImport = tuple?.item2; | ||
if (references.orphanImportOnlyFiles.containsKey(canonicalImport)) { | ||
ruleUrl = null; | ||
var url = references.orphanImportOnlyFiles[canonicalImport]?.url; | ||
var url = references.orphanImportOnlyFiles[canonicalImport!]?.url; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant canonicalImport
, not the result of Map.[]
.
lib/src/migrators/module.dart
Outdated
if (libraryUrls != null) { | ||
url = minBy(libraryUrls, (url) => url.pathSegments.length); | ||
if (libraryUrls != null && libraryUrls.isNotEmpty) { | ||
Uri? minUrl = minBy(libraryUrls, (url) => url.pathSegments.length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally I think it's cleaner to try to keep all type stuff on the RHS if possible, so if inference isn't working here I'd write:
Uri? minUrl = minBy(libraryUrls, (url) => url.pathSegments.length); | |
var minUrl = minBy<Uri>(libraryUrls, (url) => url.pathSegments.length); |
Same below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minBy
has two type parameters, so is this still cleaner as minBy<Uri, int>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think so.
No description provided.