Version 2.14.0-301.0.dev

Merge commit '0cd008082be130eeca8c868bb0a75ee5d07b25e6' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_const.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_const.dart
index 00efd4c..3dcb6c7 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_const.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_const.dart
@@ -5,8 +5,12 @@
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/source/source_range.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
 
 class AddConst extends CorrectionProducer {
   @override
@@ -38,16 +42,17 @@
       return;
     }
 
-    Future<void> insertAtOffset(AstNode targetNode) async {
+    Future<void> insertAtOffset(Expression targetNode) async {
+      var finder = _ConstRangeFinder();
+      targetNode.accept(finder);
       await builder.addDartFileEdit(file, (builder) {
         builder.addSimpleInsertion(targetNode.offset, 'const ');
+        for (var range in finder.ranges) {
+          builder.addDeletion(range);
+        }
       });
     }
 
-    // todo(pq):consider removing nested `const` declarations
-    // made unnecessary by outer ones in List, Set literal and
-    // instance creations.
-
     if (targetNode is ListLiteral) {
       await insertAtOffset(targetNode);
       return;
@@ -82,3 +87,36 @@
   /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
   static AddConst toLiteral() => AddConst(true, true);
 }
+
+class _ConstRangeFinder extends RecursiveAstVisitor<void> {
+  final List<SourceRange> ranges = [];
+
+  @override
+  void visitFunctionExpression(FunctionExpression node) {
+    // Stop visiting when we get to a closure.
+  }
+
+  @override
+  void visitInstanceCreationExpression(InstanceCreationExpression node) {
+    _removeKeyword(node.keyword);
+    super.visitInstanceCreationExpression(node);
+  }
+
+  @override
+  void visitListLiteral(ListLiteral node) {
+    _removeKeyword(node.constKeyword);
+    super.visitListLiteral(node);
+  }
+
+  @override
+  void visitSetOrMapLiteral(SetOrMapLiteral node) {
+    _removeKeyword(node.constKeyword);
+    super.visitSetOrMapLiteral(node);
+  }
+
+  void _removeKeyword(Token? keyword) {
+    if (keyword != null && keyword.type == Keyword.CONST) {
+      ranges.add(range.startStart(keyword, keyword.next!));
+    }
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_const_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_const_test.dart
index 89b7eb7..5dc2905c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_const_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_const_test.dart
@@ -177,6 +177,27 @@
 }
 ''');
   }
+
+  Future<void> test_withConstList() async {
+    await resolveTestCode('''
+class C {
+  const C(List<int> l);
+}
+void f() {
+  var c = C(const <int>[]);
+  print(c);
+}
+''');
+    await assertHasFix('''
+class C {
+  const C(List<int> l);
+}
+void f() {
+  var c = const C(<int>[]);
+  print(c);
+}
+''');
+  }
 }
 
 @reflectiveTest
@@ -272,6 +293,27 @@
 ''');
   }
 
+  Future<void> test_list_list() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+@immutable
+class C {
+  const C(List<C> children);
+}
+C c = C([C(const [])]);
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+@immutable
+class C {
+  const C(List<C> children);
+}
+C c = C(const [C([])]);
+''');
+  }
+
   Future<void> test_map() async {
     await resolveTestCode('''
 import 'package:meta/meta.dart';
@@ -282,9 +324,7 @@
   const C({required this.children});
 }
 void f() {
-  var c = C(children: {
-    1 : const {}
-  });
+  var c = C(children: {});
   print(c);
 }
 ''');
@@ -297,14 +337,33 @@
   const C({required this.children});
 }
 void f() {
-  var c = C(children: const {
-    1 : const {}
-  });
+  var c = C(children: const {});
   print(c);
 }
 ''');
   }
 
+  Future<void> test_map_map() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+@immutable
+class C {
+  const C(Map<String, C> children);
+}
+C c = C({'c': C(const {})});
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+@immutable
+class C {
+  const C(Map<String, C> children);
+}
+C c = C(const {'c': C({})});
+''');
+  }
+
   Future<void> test_set() async {
     await resolveTestCode('''
 import 'package:meta/meta.dart';
@@ -333,4 +392,25 @@
 }
 ''');
   }
+
+  Future<void> test_set_set() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+@immutable
+class C {
+  const C(Set<C> children);
+}
+C c = C({C(const {})});
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+@immutable
+class C {
+  const C(Set<C> children);
+}
+C c = C(const {C({})});
+''');
+  }
 }
diff --git a/tools/VERSION b/tools/VERSION
index fc0969a..141cbba 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 300
+PRERELEASE 301
 PRERELEASE_PATCH 0
\ No newline at end of file