Add support for boolean negation

Change-Id: Idae8d8dbedacdd0004dff3afad2d736b3a652466
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105972
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/nnbd_migration/lib/src/graph_builder.dart b/pkg/nnbd_migration/lib/src/graph_builder.dart
index 444b7f4..7730b7b 100644
--- a/pkg/nnbd_migration/lib/src/graph_builder.dart
+++ b/pkg/nnbd_migration/lib/src/graph_builder.dart
@@ -462,6 +462,12 @@
 
   @override
   DecoratedType visitPrefixExpression(PrefixExpression node) {
+    /* DecoratedType operandType = */
+    _handleAssignment(_notNullType, node.operand);
+    if (node.operator.type == TokenType.BANG) {
+      return _nonNullableBoolType;
+    }
+    // TODO(brianwilkerson) The remaining cases are invocations.
     throw new UnimplementedError('TODO(brianwilkerson)');
   }
 
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index f2d8244..d836ce9 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -1021,6 +1021,32 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_prefixExpression_bang() async {
+    var content = '''
+bool f(bool b) => !b;
+void g(bool b1, bool b2) {
+  if (b1) {
+    f(b2);
+  }
+}
+main() {
+  g(false, null);
+}
+''';
+    var expected = '''
+bool f(bool b) => !b;
+void g(bool b1, bool? b2) {
+  if (b1) {
+    f(b2!);
+  }
+}
+main() {
+  g(false, null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   test_single_file_multiple_changes() async {
     var content = '''
 int f() => null;
diff --git a/pkg/nnbd_migration/test/migration_visitor_test.dart b/pkg/nnbd_migration/test/migration_visitor_test.dart
index f7550c9c79..eb5a65e 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test.dart
+++ b/pkg/nnbd_migration/test/migration_visitor_test.dart
@@ -1062,6 +1062,21 @@
     assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
   }
 
+  test_prefixExpression_bang2() async {
+    await analyze('''
+bool f(bool b) {
+  return !b;
+}
+''');
+
+    var nullable_b = decoratedTypeAnnotation('bool b').node;
+    var check_b = checkExpression('b;');
+    assertNullCheck(check_b, nullable_b);
+
+    var return_f = decoratedTypeAnnotation('bool f').node;
+    assertEdge(never, return_f, hard: false);
+  }
+
   test_propertyAccess_return_type() async {
     await analyze('''
 class C {