Don't create linked groups when nothing was written.

In particular, if the type was invalid, so that we cannot write it.

R=brianwilkerson@google.com

Change-Id: I5499e17e2aa3527d21cab050b7fecfd3cd09efc7
Reviewed-on: https://dart-review.googlesource.com/55860
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index 8546ddb..807264c 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -184,13 +184,15 @@
     } finally {
       int end = offset + _buffer.length;
       int length = end - start;
-      Position position = new Position(fileEditBuilder.fileEdit.file, start);
-      fileEditBuilder.changeBuilder._lockedPositions.add(position);
-      LinkedEditGroup group =
-          fileEditBuilder.changeBuilder.getLinkedEditGroup(groupName);
-      group.addPosition(position, length);
-      for (LinkedEditSuggestion suggestion in builder.suggestions) {
-        group.addSuggestion(suggestion);
+      if (length != 0) {
+        Position position = new Position(fileEditBuilder.fileEdit.file, start);
+        fileEditBuilder.changeBuilder._lockedPositions.add(position);
+        LinkedEditGroup group =
+            fileEditBuilder.changeBuilder.getLinkedEditGroup(groupName);
+        group.addPosition(position, length);
+        for (LinkedEditSuggestion suggestion in builder.suggestions) {
+          group.addSuggestion(suggestion);
+        }
       }
     }
   }
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 4f5a68b..ef91dc7 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -626,26 +626,24 @@
       String groupName,
       ExecutableElement methodBeingCopied,
       bool required: false}) {
+    bool wroteType = false;
     if (type != null && !type.isDynamic) {
       if (groupName != null) {
-        bool wroteType;
         addLinkedEdit(groupName, (LinkedEditBuilder builder) {
-          // TODO(scheglov) ensure that no linked edit if nothing written
           wroteType = _writeType(type, methodBeingCopied: methodBeingCopied);
           if (wroteType && addSupertypeProposals) {
             _addSuperTypeProposals(builder, type, new Set<DartType>());
           }
         });
-        return wroteType;
       } else {
-        return _writeType(type, methodBeingCopied: methodBeingCopied);
+        wroteType = _writeType(type, methodBeingCopied: methodBeingCopied);
       }
     }
-    if (required) {
+    if (!wroteType && required) {
       write(Keyword.VAR.lexeme);
       return true;
     }
-    return false;
+    return wroteType;
   }
 
   @override
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
index fe72dfa..e662d11 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
@@ -337,7 +337,8 @@
     await builder.addFileEdit(path, (FileEditBuilder builder) {
       builder.addInsertion(10, (EditBuilder builder) {
         builder.addLinkedEdit(groupName, (LinkedEditBuilder builder) {
-          builder.addSuggestion(LinkedEditSuggestionKind.TYPE, 'A');
+          builder.write('A');
+          builder.addSuggestion(LinkedEditSuggestionKind.TYPE, 'B');
         });
       });
     });
@@ -346,13 +347,28 @@
     expect(group.suggestions, hasLength(1));
   }
 
+  test_addSuggestion_zeroLength() async {
+    String groupName = 'a';
+    ChangeBuilderImpl builder = new ChangeBuilderImpl();
+    await builder.addFileEdit(path, (FileEditBuilder builder) {
+      builder.addInsertion(10, (EditBuilder builder) {
+        builder.addLinkedEdit(groupName, (LinkedEditBuilder builder) {
+          builder.addSuggestion(LinkedEditSuggestionKind.TYPE, 'A');
+        });
+      });
+    });
+
+    expect(builder.sourceChange.linkedEditGroups, isEmpty);
+  }
+
   test_addSuggestions() async {
     String groupName = 'a';
     ChangeBuilderImpl builder = new ChangeBuilderImpl();
     await builder.addFileEdit(path, (FileEditBuilder builder) {
       builder.addInsertion(10, (EditBuilder builder) {
         builder.addLinkedEdit(groupName, (LinkedEditBuilder builder) {
-          builder.addSuggestions(LinkedEditSuggestionKind.TYPE, ['A', 'B']);
+          builder.write('A');
+          builder.addSuggestions(LinkedEditSuggestionKind.TYPE, ['B', 'C']);
         });
       });
     });
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
index 91e98bf..c7d2f7b 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
@@ -1832,6 +1832,25 @@
     expect(values, contains('C'));
   }
 
+  test_writeType_groupName_invalidType() async {
+    String path = provider.convertPath('/test.dart');
+    String content = 'class A<T> {}';
+    addSource(path, content);
+
+    InterfaceType typeA = await _getType(path, 'A');
+    DartType typeT = typeA.typeParameters.single.type;
+
+    var builder = new DartChangeBuilder(session);
+    await builder.addFileEdit(path, (builder) {
+      builder.addInsertion(content.length, (builder) {
+        // "T" cannot be written, because we are outside of "A".
+        // So, we also should not create linked groups.
+        builder.writeType(typeT, groupName: 'type');
+      });
+    });
+    expect(builder.sourceChange.linkedEditGroups, isEmpty);
+  }
+
   test_writeType_interface_typeArguments() async {
     await _assertWriteType('Map<int, List<String>>');
   }
@@ -2162,7 +2181,7 @@
     return result.element.accessors.firstWhere((v) => v.name == name);
   }
 
-  Future<DartType> _getType(String path, String name) async {
+  Future<InterfaceType> _getType(String path, String name) async {
     ClassElement classElement = await _getClassElement(path, name);
     return classElement.type;
   }