Skip to content
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

Merged
merged 7 commits into from
Apr 28, 2021
Merged

Migrate to null safety #183

merged 7 commits into from
Apr 28, 2021

Conversation

jathak
Copy link
Member

@jathak jathak commented Apr 24, 2021

No description provided.

@jathak jathak requested a review from nex3 April 26, 2021 16:25
@jathak
Copy link
Member Author

jathak commented Apr 26, 2021

Windows tests are failing due to an issue cloning a git dependency

@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 54 to 55
List<Patch> get patches => UnmodifiableListView(assertNotNull(_patches));
List<Patch>? _patches;
Copy link
Contributor

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:

Suggested change
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;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -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 >> {
Copy link
Contributor

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?

Copy link
Member Author

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);
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -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.'));
Copy link
Contributor

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).

Copy link
Member Author

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.

@@ -183,7 +190,7 @@ class Renamer<T> {
buffer.writeCharCode(next);
}
} else {
buffer.writeCharCode(char);
buffer.writeCharCode(char!);
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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]!;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intermediate variable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intermediate variable, also below

Copy link
Member Author

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")));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intermediate variable

Copy link
Member Author

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];
Copy link
Contributor

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

Copy link
Member Author

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)

@jathak jathak requested a review from nex3 April 28, 2021 17:14
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;
Copy link
Contributor

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.[].

if (libraryUrls != null) {
url = minBy(libraryUrls, (url) => url.pathSegments.length);
if (libraryUrls != null && libraryUrls.isNotEmpty) {
Uri? minUrl = minBy(libraryUrls, (url) => url.pathSegments.length);
Copy link
Contributor

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:

Suggested change
Uri? minUrl = minBy(libraryUrls, (url) => url.pathSegments.length);
var minUrl = minBy<Uri>(libraryUrls, (url) => url.pathSegments.length);

Same below.

Copy link
Member Author

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>?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think so.

@jathak jathak merged commit b4fc3d4 into master Apr 28, 2021
@jathak jathak deleted the null-safety branch April 28, 2021 23:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants