sdk
sdk copied to clipboard
Cast instead of null assertion when migrating to Null Safety
Steps to reproduce
Small repro:
Future<String> foo() async {
return null;
}
Future<int> bar() async {
var str = await foo();
return str.length;
}
What did you expect to happen?
I would've expected:
Future<String?> foo() async {
return null;
}
Future<int> bar() async {
var str = await foo();
return str!.length;
}
Or alternatively:
[...] // foo() as above
Future<int> bar() async {
var str = (await foo())!;
return str.length;
}
What actually happened?
Future<String?> foo() async {
return null;
}
Future<int > bar() async {
var str = await (foo() as FutureOr<String>);
return str.length;
}
Dart SDK version: 2.12.340383196-edge+google3-v2
cc @stereotype441
Note that this should probably be fixed because the cast inserted is actually unsound, not just ugly. It will cause problems when enabling sound null safety, repro example:
void main() async {
var y = await (foo('hello') as Future<String>);
}
Future<T?> foo<T extends Object>(T arg) async => arg;
This fails with Uncaught Error: TypeError: Instance of '_Future<String?>': type '_Future<String?>' is not a subtype of type 'Future<String>' in sound mode
I'm seeing some bogus uses of as FutureOr<String> in g3 likely caused by this issue. @stereotype441 what is the minimum we can do to prevent the tool from generating these casts? I'd be ok if the migration tool just failed to try to handle this case at all leaving this as a case for users to manually resolve.
Followed up with @stereotype441 offline and we agree that just preventing the tool from generating the as FutureOr<String> is the most realistic solution. This will cause a few analysis errors that users will need to manually have to fix but that is better than having the tool generate casts that will fail at runtime.
Fwiw, this does occur with other generic types other than String as well. These are generally responses from some API from what I have seen, and often have the word Response in the name of the type. I don't know what percentage of cases that would catch, or if you would consider dropping all casts to FutureOr<any type here>.
Fwiw, I can't think of a single valid cast to any type of FutureOr that has been generated by the tool.
Actually, I would wager that the significant majority of casts to anything with a specific generic type are invalid. I have seen similarly invalid casts to types like Iterable<String> etc that will fail at runtime.