Fix handling of `@Injectable` functions Recent changes that made migration tool more conservative affected functions used in Angular's DI: functions annotated with `@Injectable` must have non-nullable non-annotated arguments. Previously that worked just because there was no reason to make them nullable. Make such functions' arguments explicitly non-nullable. Bug: b/250862403 Change-Id: I27207994b058ba845aafd7776d14380e8104e8f8 Tested: added a test Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/289505 Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Ilya Yanok <yanok@google.com>
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart index eed9bb4..3d8f310 100644 --- a/pkg/nnbd_migration/lib/src/node_builder.dart +++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -881,8 +881,11 @@ } } } - if (declaredElement.enclosingElement is ConstructorElement && - (_isInsideAngularComponent || _isInjectable) && + final enclosingElement = declaredElement.enclosingElement; + if ((enclosingElement is ConstructorElement && + (_isInsideAngularComponent || _isInjectable) || + (enclosingElement is FunctionElement) && + _hasInjectable(enclosingElement)) && !isAnnotated) { _graph.makeNonNullable( decoratedType!.node, AngularConstructorArgumentOrigin(source, node)); @@ -976,6 +979,9 @@ declaredElement, decoratedSupertypes); } + bool _hasInjectable(Element element) => element.metadata + .any((ann) => _isAngularConstructor(ann.element, 'Injectable')); + /// Determines whether [element] is a constructor named [name] from Angular /// package. bool _isAngularConstructor(Element? element, String name) =>
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart index d4d649b..eb63db6 100644 --- a/pkg/nnbd_migration/test/api_test.dart +++ b/pkg/nnbd_migration/test/api_test.dart
@@ -556,6 +556,27 @@ await _checkSingleFileChanges(content, expected); } + Future<void> test_angular_injectable_function() async { + addAngularPackage(); + var content = ''' +import 'package:angular/angular.dart'; + +class C {} + +@Injectable() +C createC(int n, @Optional() int x) => C(); +'''; + var expected = ''' +import 'package:angular/angular.dart'; + +class C {} + +@Injectable() +C createC(int n, @Optional() int? x) => C(); +'''; + await _checkSingleFileChanges(content, expected); + } + Future<void> test_angular_optional_constructor_param() async { addAngularPackage(); var content = '''