Version 2.15.0-41.0.dev

Merge commit '86898c2ee6ad46758a43a197bcdc374591f2c5a5' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
index 95ddbc7..d5b7137 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
@@ -12,7 +12,6 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/dart/element/type_system.dart';
-import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/assist/assist.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@@ -63,27 +62,23 @@
 
   Future<void> _applyChange(
       ChangeBuilder builder, Token? keyword, int offset, DartType type) async {
-    Future<bool> tryToApplyChange(ChangeBuilder builder) async {
-      var validChange = true;
-      await builder.addDartFileEdit(file, (builder) {
+
+    _configureTargetLocation(node);
+
+    await builder.addDartFileEdit(file, (builder) {
+      if (builder.canWriteType(type)) {
         if (keyword != null && keyword.keyword == Keyword.VAR) {
           builder.addReplacement(range.token(keyword), (builder) {
-            validChange = builder.writeType(type);
+            builder.writeType(type);
           });
         } else {
           builder.addInsertion(offset, (builder) {
-            validChange = builder.writeType(type);
+            builder.writeType(type);
             builder.write(' ');
           });
         }
-      });
-      return validChange;
-    }
-
-    _configureTargetLocation(node);
-    if (await tryToApplyChange(_temporaryBuilder(builder))) {
-      await tryToApplyChange(builder);
-    }
+      }
+    });
   }
 
   /// Configure the [utils] using the given [target].
@@ -158,9 +153,6 @@
     await _applyChange(builder, declarationList.keyword, variable.offset, type);
   }
 
-  ChangeBuilder _temporaryBuilder(ChangeBuilder builder) =>
-      ChangeBuilder(workspace: (builder as ChangeBuilderImpl).workspace);
-
   DartType? _typeForVariable(VariableDeclaration variable) {
     var initializer = variable.initializer;
     if (initializer != null) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart b/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
index b6b3910..abe052d 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
@@ -6,7 +6,6 @@
 import 'package:analysis_server/src/services/correction/assist.dart';
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/assist/assist.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/range_factory.dart';
@@ -51,34 +50,26 @@
       return;
     }
 
-    Future<bool> tryToApplyChange(ChangeBuilder builder) async {
-      var validChange = true;
-      await builder.addDartFileEdit(file, (builder) {
-        if (variableList.type == null) {
-          final type = variable.declaredElement!.type;
-          if (!type.isDynamic && keyword != null) {
-            builder.addReplacement(range.token(keyword), (builder) {
-              validChange = builder.writeType(type);
-            });
+    await builder.addDartFileEdit(file, (builder) {
+      if (variableList.type == null) {
+        final type = variable.declaredElement!.type;
+        if (!type.isDynamic && keyword != null) {
+          if (!builder.canWriteType(type)) {
+            return;
           }
+          builder.addReplacement(range.token(keyword), (builder) {
+            builder.writeType(type);
+          });
         }
+      }
 
-        var indent = utils.getNodePrefix(statement);
-        var name = variable.name.name;
-        builder.addSimpleInsertion(
-            variable.name.end, ';' + eol + indent + name);
-      });
-      return validChange;
-    }
-
-    if (await tryToApplyChange(_temporaryBuilder(builder))) {
-      await tryToApplyChange(builder);
-    }
+      var indent = utils.getNodePrefix(statement);
+      var name = variable.name.name;
+      builder.addSimpleInsertion(
+          variable.name.end, ';' + eol + indent + name);
+    });
   }
 
-  ChangeBuilder _temporaryBuilder(ChangeBuilder builder) =>
-      ChangeBuilder(workspace: (builder as ChangeBuilderImpl).workspace);
-
   /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
   static SplitVariableDeclaration newInstance() => SplitVariableDeclaration();
 }
diff --git a/pkg/analysis_server/test/src/services/correction/assist/split_variable_declaration_test.dart b/pkg/analysis_server/test/src/services/correction/assist/split_variable_declaration_test.dart
index cca64e3..4fb3e72 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/split_variable_declaration_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/split_variable_declaration_test.dart
@@ -119,7 +119,7 @@
 ''');
   }
 
-  Future<void> test_unknownType() async {
+  Future<void> test_privateType() async {
     addSource('/home/test/lib/a.dart', '''
 class A {
   _B b => _B();
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 7d86a7f..0738926 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
@@ -57,6 +57,12 @@
           (builder) => buildLinkedEdit(builder as DartLinkedEditBuilder));
 
   @override
+  bool canWriteType(DartType? type, {ExecutableElement? methodBeingCopied}) =>
+      type != null && !type.isDynamic
+          ? _canWriteType(type, methodBeingCopied: methodBeingCopied)
+          : false;
+
+  @override
   LinkedEditBuilderImpl createLinkedEditBuilder() {
     return DartLinkedEditBuilderImpl(this);
   }
@@ -838,6 +844,60 @@
     }
   }
 
+  /// Check if the code to reference [type] in this compilation unit can be
+  /// written.
+  ///
+  /// See also [_writeType]
+  bool _canWriteType(DartType? type,
+      {ExecutableElement? methodBeingCopied, bool required = false}) {
+    type = _getVisibleType(type, methodBeingCopied: methodBeingCopied);
+
+    // If not a useful type, don't write it.
+    if (type == null) {
+      return false;
+    }
+    if (type.isDynamic) {
+      if (required) {
+        return true;
+      }
+      return false;
+    }
+    if (type.isBottom) {
+      var library = dartFileEditBuilder.resolvedUnit.libraryElement;
+      if (library.isNonNullableByDefault) {
+        return true;
+      }
+      return false;
+    }
+
+    var alias = type.alias;
+    if (alias != null) {
+      return true;
+    }
+
+    if (type is FunctionType) {
+      return true;
+    }
+
+    if (type is InterfaceType) {
+      return true;
+    }
+
+    if (type is NeverType) {
+      return true;
+    }
+
+    if (type is TypeParameterType) {
+      return true;
+    }
+
+    if (type is VoidType) {
+      return true;
+    }
+
+    throw UnimplementedError('(${type.runtimeType}) $type');
+  }
+
   /// Generate a name that does not occur in [existingNames] that begins with
   /// the given [prefix].
   String _generateUniqueName(Set<String> existingNames, String prefix) {
@@ -1301,6 +1361,12 @@
           range, (builder) => buildEdit(builder as DartEditBuilder));
 
   @override
+  bool canWriteType(DartType? type, {ExecutableElement? methodBeingCopied}) {
+    var builder = createEditBuilder(0, 0);
+    return builder.canWriteType(type, methodBeingCopied: methodBeingCopied);
+  }
+
+  @override
   void convertFunctionFromSyncToAsync(
       FunctionBody? body, TypeProvider typeProvider) {
     if (body == null || body.keyword != null) {
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
index af9b40a..946343c 100644
--- a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
@@ -20,6 +20,17 @@
   void addLinkedEdit(String groupName,
       void Function(DartLinkedEditBuilder builder) buildLinkedEdit);
 
+  /// Check if the code for a type annotation for the given [type] can be
+  /// written.
+  ///
+  /// If a [methodBeingCopied] is provided, then type parameters defined by that
+  /// method are assumed to be part of what is being written and hence valid
+  /// types.
+  ///
+  /// The logic is the same as the one used in [writeType]
+  bool canWriteType(DartType? type,
+      {ExecutableElement? methodBeingCopied});
+
   /// Write the code for a declaration of a class with the given [name]. If a
   /// list of [interfaces] is provided, then the class will implement those
   /// interfaces. If [isAbstract] is `true`, then the class will be abstract. If
@@ -299,6 +310,15 @@
   void addReplacement(
       SourceRange range, void Function(DartEditBuilder builder) buildEdit);
 
+  /// Check if the code for a type annotation for the given [type] can be
+  /// written.
+  ///
+  /// If a [methodBeingCopied] is provided, then type parameters defined by that
+  /// method are assumed to be part of what is being written and hence valid
+  /// types.
+  bool canWriteType(DartType? type,
+      {ExecutableElement? methodBeingCopied});
+
   /// Create one or more edits that will convert the given function [body] from
   /// being synchronous to be asynchronous. This includes adding the `async`
   /// modifier to the body as well as potentially replacing the return type of
diff --git a/tools/VERSION b/tools/VERSION
index b004d65..8a31d56 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 40
+PRERELEASE 41
 PRERELEASE_PATCH 0
\ No newline at end of file