Update dartfix NNBD to handle more situations

This CL updates the dartfix NNBD implementation to skip types
in extends, implements, on, and with clauses
in addition to addressing comments in
https://dart-review.googlesource.com/c/sdk/+/89046

Change-Id: Ifa491ac3ffd2b2ef24b1e649c1355df65adefc13
Reviewed-on: https://dart-review.googlesource.com/c/89403
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart b/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
index 14bad20..fc623af 100644
--- a/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
 import 'package:analysis_server/src/edit/edit_dartfix.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
@@ -6,6 +10,9 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 
+/// [NonNullableFix] visits each named type in a resolved compilation unit
+/// and determines whether the associated variable or parameter can be null
+/// then adds or removes a '?' trailing the named type as appropriate.
 class NonNullableFix {
   final EditDartFix dartFix;
 
@@ -62,6 +69,28 @@
   }
 
   @override
+  void visitExtendsClause(ExtendsClause node) {
+    // skip the type name associated with the extends clause
+    node.superclass?.typeArguments?.accept(this);
+  }
+
+  @override
+  void visitImplementsClause(ImplementsClause node) {
+    // skip the type names in the implements clause
+    for (TypeName typeName in node.interfaces) {
+      typeName.typeArguments?.accept(this);
+    }
+  }
+
+  @override
+  void visitOnClause(OnClause node) {
+    // skip the type name in the clause
+    for (TypeName typeName in node.superclassConstraints) {
+      typeName.typeArguments?.accept(this);
+    }
+  }
+
+  @override
   void visitTypeName(TypeName node) {
     // TODO(danrubel): Replace this braindead implementation
     // with something that determines whether or not the type should be nullable
@@ -79,4 +108,12 @@
     }
     super.visitTypeName(node);
   }
+
+  @override
+  void visitWithClause(WithClause node) {
+    // skip the type names associated with this clause
+    for (TypeName typeName in node.mixinTypes) {
+      typeName.typeArguments?.accept(this);
+    }
+  }
 }
diff --git a/pkg/analysis_server/test/domain_edit_dartfix_test.dart b/pkg/analysis_server/test/domain_edit_dartfix_test.dart
index edd543c..8e568e4 100644
--- a/pkg/analysis_server/test/domain_edit_dartfix_test.dart
+++ b/pkg/analysis_server/test/domain_edit_dartfix_test.dart
@@ -122,11 +122,18 @@
     createProject();
     addTestFile('''
 main() {
-  functionWithNullableParam(new List<Object>(1));
+  functionWithNullableParam(new List<String>(1));
   functionWithNullableParam(null);
 }
 
-void functionWithNullableParam(Object object) {
+class C1 {}
+class C2 {}
+class C extends C1 with M1 implements C2 {}
+
+mixin M1 {}
+mixin M on M1 implements C1 {}
+
+void functionWithNullableParam(String object) {
   if (object == null) {
     print('object is null');
   } else {
@@ -143,11 +150,18 @@
     expectSuggestion(result.suggestions[0], 'non-nullable', 46, 6);
     expectEdits(result.edits, '''
 main() {
-  functionWithNullableParam(new List<Object?>(1));
+  functionWithNullableParam(new List<String?>(1));
   functionWithNullableParam(null);
 }
 
-void functionWithNullableParam(Object? object) {
+class C1 {}
+class C2 {}
+class C extends C1 with M1 implements C2 {}
+
+mixin M1 {}
+mixin M on M1 implements C1 {}
+
+void functionWithNullableParam(String? object) {
   if (object == null) {
     print('object is null');
   } else {