Repository: google/protobuf.dart

Issue:
Cloning should avoid validation checks
When merging data from the same field (i.e. identical FieldInfo) we can avoid validation checks because the source must already have been validated. This will be especially beneficial for repeated fields.




Fix patch:
diff --git a/benchmarks/bin/deep_copy.dart b/benchmarks/bin/deep_copy.dart
index 09372b67..93ac7256 100644
--- a/benchmarks/bin/deep_copy.dart
+++ b/benchmarks/bin/deep_copy.dart
@@ -9,6 +9,11 @@ import 'package:protobuf_benchmarks/generated/google_message1_proto3.pb.dart'
     as p3;
 import 'package:protobuf_benchmarks/generated/google_message2.pb.dart';
 import 'package:protobuf_benchmarks/readfile.dart';
+import 'package:protobuf/protobuf.dart';
+
+GeneratedMessage? sink1;
+GeneratedMessage? sink2;
+GeneratedMessage? sink3;
 
 class Benchmark extends BenchmarkBase {
   final p2.GoogleMessage1 _message1Proto2;
@@ -26,12 +31,9 @@ class Benchmark extends BenchmarkBase {
 
   @override
   void run() {
-    // ignore: unused_result
-    _message1Proto2.deepCopy();
-    // ignore: unused_result
-    _message1Proto3.deepCopy();
-    // ignore: unused_result
-    _message2.deepCopy();
+    sink1 = _message1Proto2.deepCopy();
+    sink2 = _message1Proto3.deepCopy();
+    sink3 = _message2.deepCopy();
   }
 }
 
@@ -49,4 +51,10 @@ void main() {
     message1Proto3Input,
     message2Input,
   ).report();
+
+  if (int.parse('1') == 0) {
+    print(sink1);
+    print(sink2);
+    print(sink3);
+  }
 }
diff --git a/protobuf/CHANGELOG.md b/protobuf/CHANGELOG.md
index 7cf13e1f..83335f37 100644
--- a/protobuf/CHANGELOG.md
+++ b/protobuf/CHANGELOG.md
@@ -1,5 +1,9 @@
 ## 5.0.0-wip
 
+* Improve performance of `GeneratedMessage.deepCopy`. ([#742])
+
+[#742]: https://github.com/google/protobuf.dart/pull/742
+
 ## 4.2.0
 
 * Internal refactoring to split the package into libraries. This allows
diff --git a/protobuf/lib/src/protobuf/extension_field_set.dart b/protobuf/lib/src/protobuf/extension_field_set.dart
index 64972003..442f7709 100644
--- a/protobuf/lib/src/protobuf/extension_field_set.dart
+++ b/protobuf/lib/src/protobuf/extension_field_set.dart
@@ -6,11 +6,15 @@ part of 'internal.dart';
 
 class ExtensionFieldSet {
   final FieldSet _parent;
-  final Map<int, Extension> _info = <int, Extension>{};
-  final Map<int, dynamic> _values = <int, dynamic>{};
+  final Map<int, Extension> _info;
+  final Map<int, dynamic> _values;
   bool _isReadOnly = false;
 
-  ExtensionFieldSet(this._parent);
+  ExtensionFieldSet(this._parent)
+    : _info = <int, Extension>{},
+      _values = <int, dynamic>{};
+
+  ExtensionFieldSet._(this._parent, this._info, this._values);
 
   Extension? _getInfoOrNull(int tagNumber) => _info[tagNumber];
 
@@ -211,6 +215,34 @@ class ExtensionFieldSet {
       );
     }
   }
+
+  ExtensionFieldSet _deepCopy(FieldSet parent) {
+    final newExtensionFieldSet = ExtensionFieldSet._(
+      parent,
+      Map.from(_info),
+      Map.from(_values),
+    );
+
+    final newValues = newExtensionFieldSet._values;
+
+    for (final entry in _values.entries) {
+      final tag = entry.key;
+      final value = entry.value;
+      final fieldInfo = _info[tag]!;
+      if (fieldInfo.isMapField) {
+        final PbMap? map = value;
+        newValues[tag] = map?._deepCopy();
+      } else if (fieldInfo.isRepeated) {
+        final PbList? list = value;
+        newValues[tag] = list?._deepCopy();
+      } else if (fieldInfo.isGroupOrMessage) {
+        final GeneratedMessage? message = value;
+        newValues[tag] = message?.deepCopy();
+      }
+    }
+
+    return newExtensionFieldSet;
+  }
 }
 
 extension ExtensionFieldSetInternalExtension on ExtensionFieldSet {
diff --git a/protobuf/lib/src/protobuf/field_set.dart b/protobuf/lib/src/protobuf/field_set.dart
index 980c03c6..eeed0733 100644
--- a/protobuf/lib/src/protobuf/field_set.dart
+++ b/protobuf/lib/src/protobuf/field_set.dart
@@ -916,6 +916,64 @@ class FieldSet {
 
     _oneofCases?.addAll(original._oneofCases!);
   }
+
+  // This assumes that [this] is fresh, i.e. no extensions, no values set.
+  //
+  // The reason why this updates [this] instead of returning a new [FieldSet] is
+  // that to start cloning we need to create an empty instance via
+  // `createEmptyInstance`, which already creates an empty [FieldSet], which we
+  // reuse here.
+  void _deepCopyFrom(FieldSet original) {
+    final info = _meta;
+
+    assert(_values.length == original._values.length);
+
+    // memcpy the original's values to avoid redundant bounds checks below by
+    // copying scalar fields one by one.
+    _values.setAll(0, original._values);
+
+    for (var index = 0; index < info.byIndex.length; index++) {
+      final fieldInfo = info.byIndex[index];
+      if (fieldInfo.isMapField) {
+        final PbMap? originalMap = original._values[index];
+        if (originalMap == null) continue;
+        _values[index] = originalMap._deepCopy();
+      } else if (fieldInfo.isRepeated) {
+        final PbList? originalList = original._values[index];
+        if (originalList == null) continue;
+        _values[index] = originalList._deepCopy();
+      } else if (fieldInfo.isGroupOrMessage) {
+        final GeneratedMessage? message = original._values[index];
+        _values[index] = message?.deepCopy();
+      }
+
+      // Scalar fields are already copied above with `setAll`.
+    }
+
+    assert(_extensions == null);
+    final originalExtensions = original._extensions;
+    if (originalExtensions != null) {
+      _extensions = originalExtensions._deepCopy(this);
+    }
+
+    assert(_unknownFields == null);
+    final originalUnknownFields = original._unknownFields;
+    if (originalUnknownFields != null) {
+      _unknownFields = originalUnknownFields._deepCopy();
+    }
+
+    assert(_unknownJsonData == null);
+    final originalUnknownJsonData = original._unknownJsonData;
+    if (originalUnknownJsonData != null) {
+      _unknownJsonData = Map.from(originalUnknownJsonData);
+    }
+
+    assert(_oneofCases == null || _oneofCases.isEmpty);
+    final originalOneofCases = original._oneofCases;
+    if (originalOneofCases != null) {
+      _oneofCases!.addAll(originalOneofCases);
+    }
+  }
 }
 
 extension FieldSetInternalExtension on FieldSet {
diff --git a/protobuf/lib/src/protobuf/generated_message.dart b/protobuf/lib/src/protobuf/generated_message.dart
index 9b5c866a..5c9da770 100644
--- a/protobuf/lib/src/protobuf/generated_message.dart
+++ b/protobuf/lib/src/protobuf/generated_message.dart
@@ -48,7 +48,6 @@ abstract class GeneratedMessage {
   BuilderInfo get info_;
 
   /// Creates a deep copy of the fields in this message.
-  /// (The generated code uses [mergeFromMessage].)
   @Deprecated(
     'Using this can add significant size overhead to your binary. '
     'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
@@ -625,7 +624,11 @@ extension GeneratedMessageGenericExtensions<T extends GeneratedMessage> on T {
     '[GeneratedMessageGenericExtensions.deepCopy] '
     'does not update the message, returns a new message',
   )
-  T deepCopy() => info_.createEmptyInstance!() as T..mergeFromMessage(this);
+  T deepCopy() {
+    final newMessage = info_.createEmptyInstance!();
+    newMessage._fieldSet._deepCopyFrom(_fieldSet);
+    return newMessage as T;
+  }
 }
 
 extension GeneratedMessageInternalExtension on GeneratedMessage {
diff --git a/protobuf/lib/src/protobuf/pb_list.dart b/protobuf/lib/src/protobuf/pb_list.dart
index d48a8214..d759ce03 100644
--- a/protobuf/lib/src/protobuf/pb_list.dart
+++ b/protobuf/lib/src/protobuf/pb_list.dart
@@ -234,4 +234,20 @@ class PbList<E> extends ListBase<E> {
   static Never _readOnlyError(String methodName) {
     throw UnsupportedError("'$methodName' on a read-only list");
   }
+
+  PbList<E> _deepCopy() {
+    final newList = PbList<E>(check: _check);
+    final wrappedList = _wrappedList;
+    final newWrappedList = newList._wrappedList;
+    if (wrappedList.isNotEmpty) {
+      if (wrappedList[0] is GeneratedMessage) {
+        for (final message in wrappedList) {
+          newWrappedList.add((message as GeneratedMessage).deepCopy() as E);
+        }
+      } else {
+        newWrappedList.addAll(wrappedList);
+      }
+    }
+    return newList;
+  }
 }
diff --git a/protobuf/lib/src/protobuf/pb_map.dart b/protobuf/lib/src/protobuf/pb_map.dart
index 2816eb69..0f45c2cf 100644
--- a/protobuf/lib/src/protobuf/pb_map.dart
+++ b/protobuf/lib/src/protobuf/pb_map.dart
@@ -131,4 +131,19 @@ class PbMap<K, V> extends MapBase<K, V> {
     }
     return this;
   }
+
+  PbMap<K, V> _deepCopy() {
+    final newMap = PbMap<K, V>(keyFieldType, valueFieldType);
+    final wrappedMap = _wrappedMap;
+    final newWrappedMap = newMap._wrappedMap;
+    if (PbFieldType.isGroupOrMessage(valueFieldType)) {
+      for (final entry in wrappedMap.entries) {
+        newWrappedMap[entry.key] =
+            (entry.value as GeneratedMessage).deepCopy() as V;
+      }
+    } else {
+      newWrappedMap.addAll(wrappedMap);
+    }
+    return newMap;
+  }
 }
diff --git a/protobuf/lib/src/protobuf/unknown_field_set.dart b/protobuf/lib/src/protobuf/unknown_field_set.dart
index 196c6b4f..4c295bf0 100644
--- a/protobuf/lib/src/protobuf/unknown_field_set.dart
+++ b/protobuf/lib/src/protobuf/unknown_field_set.dart
@@ -8,11 +8,15 @@ part of 'internal.dart';
 class UnknownFieldSet {
   static final UnknownFieldSet emptyUnknownFieldSet =
       UnknownFieldSet().._markReadOnly();
-  final Map<int, UnknownFieldSetField> _fields = <int, UnknownFieldSetField>{};
 
-  UnknownFieldSet();
+  final Map<int, UnknownFieldSetField> _fields;
 
-  UnknownFieldSet._clone(UnknownFieldSet unknownFieldSet) {
+  UnknownFieldSet() : _fields = <int, UnknownFieldSetField>{};
+
+  UnknownFieldSet._(this._fields);
+
+  UnknownFieldSet._clone(UnknownFieldSet unknownFieldSet)
+    : _fields = <int, UnknownFieldSetField>{} {
     mergeFromUnknownFieldSet(unknownFieldSet);
   }
 
@@ -195,6 +199,16 @@ class UnknownFieldSet {
       _throwFrozenMessageModificationError('UnknownFieldSet', methodName);
     }
   }
+
+  UnknownFieldSet _deepCopy() {
+    Map<int, UnknownFieldSetField> newFields = {};
+    for (final entry in _fields.entries) {
+      final key = entry.key;
+      final value = entry.value;
+      newFields[key] = value._deepCopy();
+    }
+    return UnknownFieldSet._(newFields);
+  }
 }
 
 /// An unknown field in a [UnknownFieldSet].
@@ -211,6 +225,21 @@ class UnknownFieldSetField {
   List<Int64> get fixed64s => _fixed64s;
   List<UnknownFieldSet> get groups => _groups;
 
+  UnknownFieldSetField()
+    : _lengthDelimited = <List<int>>[],
+      _varints = <Int64>[],
+      _fixed32s = <int>[],
+      _fixed64s = <Int64>[],
+      _groups = <UnknownFieldSet>[];
+
+  UnknownFieldSetField._(
+    this._lengthDelimited,
+    this._varints,
+    this._fixed32s,
+    this._fixed64s,
+    this._groups,
+  );
+
   bool _isReadOnly = false;
 
   void _markReadOnly() {
@@ -309,4 +338,24 @@ class UnknownFieldSetField {
   void addVarint(Int64 value) {
     varints.add(value);
   }
+
+  UnknownFieldSetField _deepCopy() {
+    final List<List<int>> newLengthDelimited = List.from(_lengthDelimited);
+    final List<Int64> newVarints = List.from(_varints);
+    final List<int> newFixed32s = List.from(_fixed32s);
+    final List<Int64> newFixed64s = List.from(_fixed64s);
+
+    final List<UnknownFieldSet> newGroups = [];
+    for (final group in _groups) {
+      newGroups.add(group._deepCopy());
+    }
+
+    return UnknownFieldSetField._(
+      newLengthDelimited,
+      newVarints,
+      newFixed32s,
+      newFixed64s,
+      newGroups,
+    );
+  }
 }
diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md
index bf34074d..41616dad 100644
--- a/protoc_plugin/CHANGELOG.md
+++ b/protoc_plugin/CHANGELOG.md
@@ -1,6 +1,10 @@
 ## 23.0.0
 
 * Update generated code for protobuf 5.0.0.
+* Update generated `clone` members to take advantage of faster `deepCopy`
+  implementation in protobuf 5.0.0. ([#742])
+
+[#742]: https://github.com/google/protobuf.dart/pull/742
 
 ## 22.5.0
 
diff --git a/protoc_plugin/lib/src/gen/dart_options.pb.dart b/protoc_plugin/lib/src/gen/dart_options.pb.dart
index 5534342a..b92dcb35 100644
--- a/protoc_plugin/lib/src/gen/dart_options.pb.dart
+++ b/protoc_plugin/lib/src/gen/dart_options.pb.dart
@@ -50,7 +50,7 @@ class DartMixin extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  DartMixin clone() => DartMixin()..mergeFromMessage(this);
+  DartMixin clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   DartMixin copyWith(void Function(DartMixin) updates) =>
       super.copyWith((message) => updates(message as DartMixin)) as DartMixin;
@@ -130,7 +130,7 @@ class Imports extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Imports clone() => Imports()..mergeFromMessage(this);
+  Imports clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Imports copyWith(void Function(Imports) updates) =>
       super.copyWith((message) => updates(message as Imports)) as Imports;
diff --git a/protoc_plugin/lib/src/gen/google/api/client.pb.dart b/protoc_plugin/lib/src/gen/google/api/client.pb.dart
index 4660db8c..fdf9cbe7 100644
--- a/protoc_plugin/lib/src/gen/google/api/client.pb.dart
+++ b/protoc_plugin/lib/src/gen/google/api/client.pb.dart
@@ -64,8 +64,7 @@ class CommonLanguageSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  CommonLanguageSettings clone() =>
-      CommonLanguageSettings()..mergeFromMessage(this);
+  CommonLanguageSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   CommonLanguageSettings copyWith(
           void Function(CommonLanguageSettings) updates) =>
@@ -188,8 +187,7 @@ class ClientLibrarySettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  ClientLibrarySettings clone() =>
-      ClientLibrarySettings()..mergeFromMessage(this);
+  ClientLibrarySettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   ClientLibrarySettings copyWith(
           void Function(ClientLibrarySettings) updates) =>
@@ -411,7 +409,7 @@ class Publishing extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Publishing clone() => Publishing()..mergeFromMessage(this);
+  Publishing clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Publishing copyWith(void Function(Publishing) updates) =>
       super.copyWith((message) => updates(message as Publishing)) as Publishing;
@@ -576,7 +574,7 @@ class JavaSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  JavaSettings clone() => JavaSettings()..mergeFromMessage(this);
+  JavaSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   JavaSettings copyWith(void Function(JavaSettings) updates) =>
       super.copyWith((message) => updates(message as JavaSettings))
@@ -674,7 +672,7 @@ class CppSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  CppSettings clone() => CppSettings()..mergeFromMessage(this);
+  CppSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   CppSettings copyWith(void Function(CppSettings) updates) =>
       super.copyWith((message) => updates(message as CppSettings))
@@ -734,7 +732,7 @@ class PhpSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  PhpSettings clone() => PhpSettings()..mergeFromMessage(this);
+  PhpSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   PhpSettings copyWith(void Function(PhpSettings) updates) =>
       super.copyWith((message) => updates(message as PhpSettings))
@@ -805,8 +803,7 @@ class PythonSettings_ExperimentalFeatures extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  PythonSettings_ExperimentalFeatures clone() =>
-      PythonSettings_ExperimentalFeatures()..mergeFromMessage(this);
+  PythonSettings_ExperimentalFeatures clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   PythonSettings_ExperimentalFeatures copyWith(
           void Function(PythonSettings_ExperimentalFeatures) updates) =>
@@ -904,7 +901,7 @@ class PythonSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  PythonSettings clone() => PythonSettings()..mergeFromMessage(this);
+  PythonSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   PythonSettings copyWith(void Function(PythonSettings) updates) =>
       super.copyWith((message) => updates(message as PythonSettings))
@@ -979,7 +976,7 @@ class NodeSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  NodeSettings clone() => NodeSettings()..mergeFromMessage(this);
+  NodeSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   NodeSettings copyWith(void Function(NodeSettings) updates) =>
       super.copyWith((message) => updates(message as NodeSettings))
@@ -1070,7 +1067,7 @@ class DotnetSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  DotnetSettings clone() => DotnetSettings()..mergeFromMessage(this);
+  DotnetSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   DotnetSettings copyWith(void Function(DotnetSettings) updates) =>
       super.copyWith((message) => updates(message as DotnetSettings))
@@ -1166,7 +1163,7 @@ class RubySettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  RubySettings clone() => RubySettings()..mergeFromMessage(this);
+  RubySettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   RubySettings copyWith(void Function(RubySettings) updates) =>
       super.copyWith((message) => updates(message as RubySettings))
@@ -1235,7 +1232,7 @@ class GoSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  GoSettings clone() => GoSettings()..mergeFromMessage(this);
+  GoSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   GoSettings copyWith(void Function(GoSettings) updates) =>
       super.copyWith((message) => updates(message as GoSettings)) as GoSettings;
@@ -1322,8 +1319,7 @@ class MethodSettings_LongRunning extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  MethodSettings_LongRunning clone() =>
-      MethodSettings_LongRunning()..mergeFromMessage(this);
+  MethodSettings_LongRunning clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   MethodSettings_LongRunning copyWith(
           void Function(MethodSettings_LongRunning) updates) =>
@@ -1432,7 +1428,7 @@ class MethodSettings extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  MethodSettings clone() => MethodSettings()..mergeFromMessage(this);
+  MethodSettings clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   MethodSettings copyWith(void Function(MethodSettings) updates) =>
       super.copyWith((message) => updates(message as MethodSettings))
@@ -1542,8 +1538,7 @@ class SelectiveGapicGeneration extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  SelectiveGapicGeneration clone() =>
-      SelectiveGapicGeneration()..mergeFromMessage(this);
+  SelectiveGapicGeneration clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   SelectiveGapicGeneration copyWith(
           void Function(SelectiveGapicGeneration) updates) =>
diff --git a/protoc_plugin/lib/src/gen/google/api/http.pb.dart b/protoc_plugin/lib/src/gen/google/api/http.pb.dart
index 2fa07e33..018fa1a0 100644
--- a/protoc_plugin/lib/src/gen/google/api/http.pb.dart
+++ b/protoc_plugin/lib/src/gen/google/api/http.pb.dart
@@ -50,7 +50,7 @@ class Http extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Http clone() => Http()..mergeFromMessage(this);
+  Http clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Http copyWith(void Function(Http) updates) =>
       super.copyWith((message) => updates(message as Http)) as Http;
@@ -423,7 +423,7 @@ class HttpRule extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  HttpRule clone() => HttpRule()..mergeFromMessage(this);
+  HttpRule clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   HttpRule copyWith(void Function(HttpRule) updates) =>
       super.copyWith((message) => updates(message as HttpRule)) as HttpRule;
@@ -590,7 +590,7 @@ class CustomHttpPattern extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  CustomHttpPattern clone() => CustomHttpPattern()..mergeFromMessage(this);
+  CustomHttpPattern clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   CustomHttpPattern copyWith(void Function(CustomHttpPattern) updates) =>
       super.copyWith((message) => updates(message as CustomHttpPattern))
diff --git a/protoc_plugin/lib/src/gen/google/api/routing.pb.dart b/protoc_plugin/lib/src/gen/google/api/routing.pb.dart
index c8c7627e..528ac007 100644
--- a/protoc_plugin/lib/src/gen/google/api/routing.pb.dart
+++ b/protoc_plugin/lib/src/gen/google/api/routing.pb.dart
@@ -404,7 +404,7 @@ class RoutingRule extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  RoutingRule clone() => RoutingRule()..mergeFromMessage(this);
+  RoutingRule clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   RoutingRule copyWith(void Function(RoutingRule) updates) =>
       super.copyWith((message) => updates(message as RoutingRule))
@@ -463,7 +463,7 @@ class RoutingParameter extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  RoutingParameter clone() => RoutingParameter()..mergeFromMessage(this);
+  RoutingParameter clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   RoutingParameter copyWith(void Function(RoutingParameter) updates) =>
       super.copyWith((message) => updates(message as RoutingParameter))
diff --git a/protoc_plugin/lib/src/gen/google/protobuf/compiler/plugin.pb.dart b/protoc_plugin/lib/src/gen/google/protobuf/compiler/plugin.pb.dart
index 023287aa..bbdcac1c 100644
--- a/protoc_plugin/lib/src/gen/google/protobuf/compiler/plugin.pb.dart
+++ b/protoc_plugin/lib/src/gen/google/protobuf/compiler/plugin.pb.dart
@@ -58,7 +58,7 @@ class Version extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Version clone() => Version()..mergeFromMessage(this);
+  Version clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Version copyWith(void Function(Version) updates) =>
       super.copyWith((message) => updates(message as Version)) as Version;
@@ -160,8 +160,7 @@ class CodeGeneratorRequest extends $pb.GeneratedMessage {
         subBuilder: $0.FileDescriptorProto.create);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  CodeGeneratorRequest clone() =>
-      CodeGeneratorRequest()..mergeFromMessage(this);
+  CodeGeneratorRequest clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   CodeGeneratorRequest copyWith(void Function(CodeGeneratorRequest) updates) =>
       super.copyWith((message) => updates(message as CodeGeneratorRequest))
@@ -276,8 +275,7 @@ class CodeGeneratorResponse_File extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  CodeGeneratorResponse_File clone() =>
-      CodeGeneratorResponse_File()..mergeFromMessage(this);
+  CodeGeneratorResponse_File clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   CodeGeneratorResponse_File copyWith(
           void Function(CodeGeneratorResponse_File) updates) =>
@@ -436,8 +434,7 @@ class CodeGeneratorResponse extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  CodeGeneratorResponse clone() =>
-      CodeGeneratorResponse()..mergeFromMessage(this);
+  CodeGeneratorResponse clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   CodeGeneratorResponse copyWith(
           void Function(CodeGeneratorResponse) updates) =>
diff --git a/protoc_plugin/lib/src/gen/google/protobuf/descriptor.pb.dart b/protoc_plugin/lib/src/gen/google/protobuf/descriptor.pb.dart
index fe6bf471..4e051934 100644
--- a/protoc_plugin/lib/src/gen/google/protobuf/descriptor.pb.dart
+++ b/protoc_plugin/lib/src/gen/google/protobuf/descriptor.pb.dart
@@ -52,7 +52,7 @@ class FileDescriptorSet extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FileDescriptorSet clone() => FileDescriptorSet()..mergeFromMessage(this);
+  FileDescriptorSet clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FileDescriptorSet copyWith(void Function(FileDescriptorSet) updates) =>
       super.copyWith((message) => updates(message as FileDescriptorSet))
@@ -159,7 +159,7 @@ class FileDescriptorProto extends $pb.GeneratedMessage {
     ..pPS(15, _omitFieldNames ? '' : 'optionDependency');
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FileDescriptorProto clone() => FileDescriptorProto()..mergeFromMessage(this);
+  FileDescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FileDescriptorProto copyWith(void Function(FileDescriptorProto) updates) =>
       super.copyWith((message) => updates(message as FileDescriptorProto))
@@ -317,8 +317,7 @@ class DescriptorProto_ExtensionRange extends $pb.GeneratedMessage {
         subBuilder: ExtensionRangeOptions.create);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  DescriptorProto_ExtensionRange clone() =>
-      DescriptorProto_ExtensionRange()..mergeFromMessage(this);
+  DescriptorProto_ExtensionRange clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   DescriptorProto_ExtensionRange copyWith(
           void Function(DescriptorProto_ExtensionRange) updates) =>
@@ -404,8 +403,7 @@ class DescriptorProto_ReservedRange extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  DescriptorProto_ReservedRange clone() =>
-      DescriptorProto_ReservedRange()..mergeFromMessage(this);
+  DescriptorProto_ReservedRange clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   DescriptorProto_ReservedRange copyWith(
           void Function(DescriptorProto_ReservedRange) updates) =>
@@ -523,7 +521,7 @@ class DescriptorProto extends $pb.GeneratedMessage {
         enumValues: SymbolVisibility.values);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  DescriptorProto clone() => DescriptorProto()..mergeFromMessage(this);
+  DescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   DescriptorProto copyWith(void Function(DescriptorProto) updates) =>
       super.copyWith((message) => updates(message as DescriptorProto))
@@ -640,8 +638,7 @@ class ExtensionRangeOptions_Declaration extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  ExtensionRangeOptions_Declaration clone() =>
-      ExtensionRangeOptions_Declaration()..mergeFromMessage(this);
+  ExtensionRangeOptions_Declaration clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   ExtensionRangeOptions_Declaration copyWith(
           void Function(ExtensionRangeOptions_Declaration) updates) =>
@@ -768,8 +765,7 @@ class ExtensionRangeOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  ExtensionRangeOptions clone() =>
-      ExtensionRangeOptions()..mergeFromMessage(this);
+  ExtensionRangeOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   ExtensionRangeOptions copyWith(
           void Function(ExtensionRangeOptions) updates) =>
@@ -892,8 +888,7 @@ class FieldDescriptorProto extends $pb.GeneratedMessage {
     ..aOB(17, _omitFieldNames ? '' : 'proto3Optional');
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FieldDescriptorProto clone() =>
-      FieldDescriptorProto()..mergeFromMessage(this);
+  FieldDescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FieldDescriptorProto copyWith(void Function(FieldDescriptorProto) updates) =>
       super.copyWith((message) => updates(message as FieldDescriptorProto))
@@ -1086,8 +1081,7 @@ class OneofDescriptorProto extends $pb.GeneratedMessage {
         subBuilder: OneofOptions.create);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  OneofDescriptorProto clone() =>
-      OneofDescriptorProto()..mergeFromMessage(this);
+  OneofDescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   OneofDescriptorProto copyWith(void Function(OneofDescriptorProto) updates) =>
       super.copyWith((message) => updates(message as OneofDescriptorProto))
@@ -1165,8 +1159,7 @@ class EnumDescriptorProto_EnumReservedRange extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  EnumDescriptorProto_EnumReservedRange clone() =>
-      EnumDescriptorProto_EnumReservedRange()..mergeFromMessage(this);
+  EnumDescriptorProto_EnumReservedRange clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   EnumDescriptorProto_EnumReservedRange copyWith(
           void Function(EnumDescriptorProto_EnumReservedRange) updates) =>
@@ -1260,7 +1253,7 @@ class EnumDescriptorProto extends $pb.GeneratedMessage {
         enumValues: SymbolVisibility.values);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  EnumDescriptorProto clone() => EnumDescriptorProto()..mergeFromMessage(this);
+  EnumDescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   EnumDescriptorProto copyWith(void Function(EnumDescriptorProto) updates) =>
       super.copyWith((message) => updates(message as EnumDescriptorProto))
@@ -1360,8 +1353,7 @@ class EnumValueDescriptorProto extends $pb.GeneratedMessage {
         subBuilder: EnumValueOptions.create);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  EnumValueDescriptorProto clone() =>
-      EnumValueDescriptorProto()..mergeFromMessage(this);
+  EnumValueDescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   EnumValueDescriptorProto copyWith(
           void Function(EnumValueDescriptorProto) updates) =>
@@ -1448,8 +1440,7 @@ class ServiceDescriptorProto extends $pb.GeneratedMessage {
         subBuilder: ServiceOptions.create);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  ServiceDescriptorProto clone() =>
-      ServiceDescriptorProto()..mergeFromMessage(this);
+  ServiceDescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   ServiceDescriptorProto copyWith(
           void Function(ServiceDescriptorProto) updates) =>
@@ -1537,8 +1528,7 @@ class MethodDescriptorProto extends $pb.GeneratedMessage {
     ..aOB(6, _omitFieldNames ? '' : 'serverStreaming');
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  MethodDescriptorProto clone() =>
-      MethodDescriptorProto()..mergeFromMessage(this);
+  MethodDescriptorProto clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   MethodDescriptorProto copyWith(
           void Function(MethodDescriptorProto) updates) =>
@@ -1723,7 +1713,7 @@ class FileOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FileOptions clone() => FileOptions()..mergeFromMessage(this);
+  FileOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FileOptions copyWith(void Function(FileOptions) updates) =>
       super.copyWith((message) => updates(message as FileOptions))
@@ -2057,7 +2047,7 @@ class MessageOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  MessageOptions clone() => MessageOptions()..mergeFromMessage(this);
+  MessageOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   MessageOptions copyWith(void Function(MessageOptions) updates) =>
       super.copyWith((message) => updates(message as MessageOptions))
@@ -2236,8 +2226,7 @@ class FieldOptions_EditionDefault extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FieldOptions_EditionDefault clone() =>
-      FieldOptions_EditionDefault()..mergeFromMessage(this);
+  FieldOptions_EditionDefault clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FieldOptions_EditionDefault copyWith(
           void Function(FieldOptions_EditionDefault) updates) =>
@@ -2328,8 +2317,7 @@ class FieldOptions_FeatureSupport extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FieldOptions_FeatureSupport clone() =>
-      FieldOptions_FeatureSupport()..mergeFromMessage(this);
+  FieldOptions_FeatureSupport clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FieldOptions_FeatureSupport copyWith(
           void Function(FieldOptions_FeatureSupport) updates) =>
@@ -2489,7 +2477,7 @@ class FieldOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FieldOptions clone() => FieldOptions()..mergeFromMessage(this);
+  FieldOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FieldOptions copyWith(void Function(FieldOptions) updates) =>
       super.copyWith((message) => updates(message as FieldOptions))
@@ -2719,7 +2707,7 @@ class OneofOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  OneofOptions clone() => OneofOptions()..mergeFromMessage(this);
+  OneofOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   OneofOptions copyWith(void Function(OneofOptions) updates) =>
       super.copyWith((message) => updates(message as OneofOptions))
@@ -2805,7 +2793,7 @@ class EnumOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  EnumOptions clone() => EnumOptions()..mergeFromMessage(this);
+  EnumOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   EnumOptions copyWith(void Function(EnumOptions) updates) =>
       super.copyWith((message) => updates(message as EnumOptions))
@@ -2933,7 +2921,7 @@ class EnumValueOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  EnumValueOptions clone() => EnumValueOptions()..mergeFromMessage(this);
+  EnumValueOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   EnumValueOptions copyWith(void Function(EnumValueOptions) updates) =>
       super.copyWith((message) => updates(message as EnumValueOptions))
@@ -3047,7 +3035,7 @@ class ServiceOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  ServiceOptions clone() => ServiceOptions()..mergeFromMessage(this);
+  ServiceOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   ServiceOptions copyWith(void Function(ServiceOptions) updates) =>
       super.copyWith((message) => updates(message as ServiceOptions))
@@ -3144,7 +3132,7 @@ class MethodOptions extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  MethodOptions clone() => MethodOptions()..mergeFromMessage(this);
+  MethodOptions clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   MethodOptions copyWith(void Function(MethodOptions) updates) =>
       super.copyWith((message) => updates(message as MethodOptions))
@@ -3242,8 +3230,7 @@ class UninterpretedOption_NamePart extends $pb.GeneratedMessage {
         2, _omitFieldNames ? '' : 'isExtension', $pb.PbFieldType.QB);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  UninterpretedOption_NamePart clone() =>
-      UninterpretedOption_NamePart()..mergeFromMessage(this);
+  UninterpretedOption_NamePart clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   UninterpretedOption_NamePart copyWith(
           void Function(UninterpretedOption_NamePart) updates) =>
@@ -3341,7 +3328,7 @@ class UninterpretedOption extends $pb.GeneratedMessage {
     ..aOS(8, _omitFieldNames ? '' : 'aggregateValue');
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  UninterpretedOption clone() => UninterpretedOption()..mergeFromMessage(this);
+  UninterpretedOption clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   UninterpretedOption copyWith(void Function(UninterpretedOption) updates) =>
       super.copyWith((message) => updates(message as UninterpretedOption))
@@ -3441,8 +3428,7 @@ class FeatureSet_VisibilityFeature extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FeatureSet_VisibilityFeature clone() =>
-      FeatureSet_VisibilityFeature()..mergeFromMessage(this);
+  FeatureSet_VisibilityFeature clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FeatureSet_VisibilityFeature copyWith(
           void Function(FeatureSet_VisibilityFeature) updates) =>
@@ -3559,7 +3545,7 @@ class FeatureSet extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FeatureSet clone() => FeatureSet()..mergeFromMessage(this);
+  FeatureSet clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FeatureSet copyWith(void Function(FeatureSet) updates) =>
       super.copyWith((message) => updates(message as FeatureSet)) as FeatureSet;
@@ -3699,8 +3685,7 @@ class FeatureSetDefaults_FeatureSetEditionDefault extends $pb.GeneratedMessage {
         subBuilder: FeatureSet.create);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FeatureSetDefaults_FeatureSetEditionDefault clone() =>
-      FeatureSetDefaults_FeatureSetEditionDefault()..mergeFromMessage(this);
+  FeatureSetDefaults_FeatureSetEditionDefault clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FeatureSetDefaults_FeatureSetEditionDefault copyWith(
           void Function(FeatureSetDefaults_FeatureSetEditionDefault) updates) =>
@@ -3803,7 +3788,7 @@ class FeatureSetDefaults extends $pb.GeneratedMessage {
         enumValues: Edition.values);
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  FeatureSetDefaults clone() => FeatureSetDefaults()..mergeFromMessage(this);
+  FeatureSetDefaults clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   FeatureSetDefaults copyWith(void Function(FeatureSetDefaults) updates) =>
       super.copyWith((message) => updates(message as FeatureSetDefaults))
@@ -3890,8 +3875,7 @@ class SourceCodeInfo_Location extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  SourceCodeInfo_Location clone() =>
-      SourceCodeInfo_Location()..mergeFromMessage(this);
+  SourceCodeInfo_Location clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   SourceCodeInfo_Location copyWith(
           void Function(SourceCodeInfo_Location) updates) =>
@@ -4046,7 +4030,7 @@ class SourceCodeInfo extends $pb.GeneratedMessage {
     ..hasExtensions = true;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  SourceCodeInfo clone() => SourceCodeInfo()..mergeFromMessage(this);
+  SourceCodeInfo clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   SourceCodeInfo copyWith(void Function(SourceCodeInfo) updates) =>
       super.copyWith((message) => updates(message as SourceCodeInfo))
@@ -4156,8 +4140,7 @@ class GeneratedCodeInfo_Annotation extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  GeneratedCodeInfo_Annotation clone() =>
-      GeneratedCodeInfo_Annotation()..mergeFromMessage(this);
+  GeneratedCodeInfo_Annotation clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   GeneratedCodeInfo_Annotation copyWith(
           void Function(GeneratedCodeInfo_Annotation) updates) =>
@@ -4261,7 +4244,7 @@ class GeneratedCodeInfo extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  GeneratedCodeInfo clone() => GeneratedCodeInfo()..mergeFromMessage(this);
+  GeneratedCodeInfo clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   GeneratedCodeInfo copyWith(void Function(GeneratedCodeInfo) updates) =>
       super.copyWith((message) => updates(message as GeneratedCodeInfo))
diff --git a/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart b/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart
index a60ed95b..343ec9dd 100644
--- a/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart
+++ b/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart
@@ -108,7 +108,7 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Duration clone() => Duration()..mergeFromMessage(this);
+  Duration clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Duration copyWith(void Function(Duration) updates) =>
       super.copyWith((message) => updates(message as Duration)) as Duration;
diff --git a/protoc_plugin/lib/src/message_generator.dart b/protoc_plugin/lib/src/message_generator.dart
index d3fb0ebb..e6623879 100644
--- a/protoc_plugin/lib/src/message_generator.dart
+++ b/protoc_plugin/lib/src/message_generator.dart
@@ -473,9 +473,7 @@ class MessageGenerator extends ProtobufContainer {
           '@$coreImportPrefix.Deprecated('
           "'See https://github.com/google/protobuf.dart/issues/998.')",
         );
-        out.println(
-          '$classname clone() => $classname()..mergeFromMessage(this);',
-        );
+        out.println('$classname clone() => deepCopy();');
         out.println(
           '@$coreImportPrefix.Deprecated('
           "'See https://github.com/google/protobuf.dart/issues/998.')",


Test patch:
diff --git a/protoc_plugin/test/generated_message_test.dart b/protoc_plugin/test/generated_message_test.dart
index 13683a38..1a769a32 100644
--- a/protoc_plugin/test/generated_message_test.dart
+++ b/protoc_plugin/test/generated_message_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:protobuf/protobuf.dart';
 import 'package:test/test.dart';
+import 'package:fixnum/fixnum.dart';
 
 import 'gen/duplicate_names_import.pb.dart';
 import 'gen/enums.pb.dart';
@@ -811,6 +812,27 @@ void main() {
     assertAllExtensionsSet(value2);
   });
 
+  test('clone extensions', () {
+    final value1 = TestAllExtensions();
+    setAllExtensions(value1);
+    // ignore: deprecated_member_use_from_same_package
+    final value2 = value1.clone();
+    assertAllExtensionsSet(value2);
+  });
+
+  test('mergeFromMessage', () {
+    final value1 = getAllSet();
+    final value2 = TestAllTypes()..mergeFromMessage(value1);
+    testCopy(value1, value2);
+  });
+
+  test('mergeFromMessage extensions', () {
+    final value1 = TestAllExtensions();
+    setAllExtensions(value1);
+    final value2 = TestAllExtensions()..mergeFromMessage(value1);
+    assertAllExtensionsSet(value2);
+  });
+
   test('Handling enums defined out of order', () {
     final message = MessageWithEnums();
     for (final enum_ in DenseEnum.values) {
@@ -877,3 +899,120 @@ void testCopy(TestAllTypes value1, TestAllTypes value2) {
 
   assertAllFieldsSet(value1);
 }
+
+/// Same as `testCopy`, but tests extension fields.
+///
+/// `value2` should be the clone of `value1`, either with `clone` or `deepCopy`.
+void testCopyExtensions(TestAllExtensions value1, TestAllExtensions value2) {
+  assertAllExtensionsSet(value2);
+  expect(value2, isNot(same(value1)));
+
+  value2.setExtension(
+    Unittest.optionalInt32Extension,
+    value2.getExtension(Unittest.optionalInt32Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalInt64Extension,
+    value2.getExtension(Unittest.optionalInt64Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalUint32Extension,
+    value2.getExtension(Unittest.optionalUint32Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalUint64Extension,
+    value2.getExtension(Unittest.optionalUint64Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalSint32Extension,
+    value2.getExtension(Unittest.optionalSint32Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalSint64Extension,
+    value2.getExtension(Unittest.optionalSint64Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalFixed32Extension,
+    value2.getExtension(Unittest.optionalFixed32Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalFixed64Extension,
+    value2.getExtension(Unittest.optionalFixed64Extension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalFloatExtension,
+    value2.getExtension(Unittest.optionalFloatExtension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalDoubleExtension,
+    value2.getExtension(Unittest.optionalDoubleExtension) + 1,
+  );
+  value2.setExtension(
+    Unittest.optionalBoolExtension,
+    !value2.getExtension(Unittest.optionalBoolExtension),
+  );
+  value2.setExtension(Unittest.optionalStringExtension, "hi 1");
+
+  // Same as `testCopy`, don't test `bytes` fields.
+  // value2.getExtension(Unittest.optionalBytesExtension).add(987);
+
+  value2.getExtension(Unittest.optionalGroupExtension).a += 1;
+  value2.getExtension(Unittest.optionalNestedMessageExtension).bb += 1;
+  value2.getExtension(Unittest.optionalForeignMessageExtension).c += 1;
+  value2.getExtension(Unittest.optionalImportMessageExtension).d += 1;
+  value2.setExtension(
+    Unittest.optionalNestedEnumExtension,
+    TestAllTypes_NestedEnum.BAR,
+  );
+  value2.setExtension(
+    Unittest.optionalForeignEnumExtension,
+    ForeignEnum.FOREIGN_BAR,
+  );
+  value2.setExtension(
+    Unittest.optionalImportEnumExtension,
+    ImportEnum.IMPORT_BAR,
+  );
+  value2.setExtension(Unittest.optionalStringPieceExtension, "hi 2");
+  value2.setExtension(Unittest.optionalCordExtension, "hi 3");
+
+  value2.getExtension(Unittest.repeatedInt32Extension).add(123);
+  value2.getExtension(Unittest.repeatedInt64Extension).add(Int64(123));
+  value2.getExtension(Unittest.repeatedUint32Extension).add(123);
+  value2.getExtension(Unittest.repeatedUint64Extension).add(Int64(123));
+  value2.getExtension(Unittest.repeatedSint32Extension).add(123);
+  value2.getExtension(Unittest.repeatedSint64Extension).add(Int64(123));
+  value2.getExtension(Unittest.repeatedFixed32Extension).add(123);
+  value2.getExtension(Unittest.repeatedFixed64Extension).add(Int64(123));
+  value2.getExtension(Unittest.repeatedSfixed32Extension).add(123);
+  value2.getExtension(Unittest.repeatedSfixed64Extension).add(Int64(123));
+  value2.getExtension(Unittest.repeatedFloatExtension).add(123);
+  value2.getExtension(Unittest.repeatedDoubleExtension).add(123);
+  value2.getExtension(Unittest.repeatedDoubleExtension).add(true);
+  value2.getExtension(Unittest.repeatedStringExtension).add("hi 4");
+  value2.getExtension(Unittest.repeatedBytesExtension).add(<int>[1, 2, 3]);
+  value2
+      .getExtension(Unittest.repeatedGroupExtension)
+      .add(RepeatedGroup_extension());
+  value2
+      .getExtension(Unittest.repeatedNestedMessageExtension)
+      .add(TestAllTypes_NestedMessage());
+  value2
+      .getExtension(Unittest.repeatedForeignMessageExtension)
+      .add(ForeignMessage());
+  value2
+      .getExtension(Unittest.repeatedImportMessageExtension)
+      .add(ImportMessage());
+  value2
+      .getExtension(Unittest.repeatedNestedEnumExtension)
+      .add(TestAllTypes_NestedEnum.BAR);
+  value2
+      .getExtension(Unittest.repeatedForeignEnumExtension)
+      .add(ForeignEnum.FOREIGN_BAR);
+  value2
+      .getExtension(Unittest.repeatedImportEnumExtension)
+      .add(ImportEnum.IMPORT_BAR);
+  value2.getExtension(Unittest.repeatedStringPieceExtension).add("hi 5");
+  value2.getExtension(Unittest.repeatedCordExtension).add("hi 6");
+
+  assertAllExtensionsSet(value1);
+}
diff --git a/protoc_plugin/test/goldens/deprecations.pb.dart b/protoc_plugin/test/goldens/deprecations.pb.dart
index 60c0dfa8..96fdc2e8 100644
--- a/protoc_plugin/test/goldens/deprecations.pb.dart
+++ b/protoc_plugin/test/goldens/deprecations.pb.dart
@@ -46,7 +46,7 @@ class HelloRequest extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  HelloRequest clone() => HelloRequest()..mergeFromMessage(this);
+  HelloRequest clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   HelloRequest copyWith(void Function(HelloRequest) updates) =>
       super.copyWith((message) => updates(message as HelloRequest))
@@ -106,7 +106,7 @@ class HelloReply extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  HelloReply clone() => HelloReply()..mergeFromMessage(this);
+  HelloReply clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   HelloReply copyWith(void Function(HelloReply) updates) =>
       super.copyWith((message) => updates(message as HelloReply)) as HelloReply;
diff --git a/protoc_plugin/test/goldens/doc_comments.pb.dart b/protoc_plugin/test/goldens/doc_comments.pb.dart
index 8688ddec..245a2030 100644
--- a/protoc_plugin/test/goldens/doc_comments.pb.dart
+++ b/protoc_plugin/test/goldens/doc_comments.pb.dart
@@ -46,7 +46,7 @@ class HelloRequest extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  HelloRequest clone() => HelloRequest()..mergeFromMessage(this);
+  HelloRequest clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   HelloRequest copyWith(void Function(HelloRequest) updates) =>
       super.copyWith((message) => updates(message as HelloRequest))
@@ -103,7 +103,7 @@ class HelloReply extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  HelloReply clone() => HelloReply()..mergeFromMessage(this);
+  HelloReply clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   HelloReply copyWith(void Function(HelloReply) updates) =>
       super.copyWith((message) => updates(message as HelloReply)) as HelloReply;
diff --git a/protoc_plugin/test/goldens/grpc_service.pb.dart b/protoc_plugin/test/goldens/grpc_service.pb.dart
index cfb2b226..d7569635 100644
--- a/protoc_plugin/test/goldens/grpc_service.pb.dart
+++ b/protoc_plugin/test/goldens/grpc_service.pb.dart
@@ -34,7 +34,7 @@ class Empty extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Empty clone() => Empty()..mergeFromMessage(this);
+  Empty clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Empty copyWith(void Function(Empty) updates) =>
       super.copyWith((message) => updates(message as Empty)) as Empty;
diff --git a/protoc_plugin/test/goldens/imports.pb.dart b/protoc_plugin/test/goldens/imports.pb.dart
index dcc8d340..f9b26f9d 100644
--- a/protoc_plugin/test/goldens/imports.pb.dart
+++ b/protoc_plugin/test/goldens/imports.pb.dart
@@ -39,7 +39,7 @@ class M extends $pb.GeneratedMessage {
         ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  M clone() => M()..mergeFromMessage(this);
+  M clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   M copyWith(void Function(M) updates) =>
       super.copyWith((message) => updates(message as M)) as M;
diff --git a/protoc_plugin/test/goldens/int64.pb.dart b/protoc_plugin/test/goldens/int64.pb.dart
index d7eefab9..a5a4a037 100644
--- a/protoc_plugin/test/goldens/int64.pb.dart
+++ b/protoc_plugin/test/goldens/int64.pb.dart
@@ -36,7 +36,7 @@ class Int64 extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Int64 clone() => Int64()..mergeFromMessage(this);
+  Int64 clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Int64 copyWith(void Function(Int64) updates) =>
       super.copyWith((message) => updates(message as Int64)) as Int64;
diff --git a/protoc_plugin/test/goldens/messageGenerator.pb.dart b/protoc_plugin/test/goldens/messageGenerator.pb.dart
index 15d05a34..dd4ef600 100644
--- a/protoc_plugin/test/goldens/messageGenerator.pb.dart
+++ b/protoc_plugin/test/goldens/messageGenerator.pb.dart
@@ -14,7 +14,7 @@ class PhoneNumber extends $pb.GeneratedMessage {
   ;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  PhoneNumber clone() => PhoneNumber()..mergeFromMessage(this);
+  PhoneNumber clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   PhoneNumber copyWith(void Function(PhoneNumber) updates) => super.copyWith((message) => updates(message as PhoneNumber)) as PhoneNumber;
 
diff --git a/protoc_plugin/test/goldens/messageGenerator.pb.dart.meta b/protoc_plugin/test/goldens/messageGenerator.pb.dart.meta
index faefae9b..fad54eac 100644
--- a/protoc_plugin/test/goldens/messageGenerator.pb.dart.meta
+++ b/protoc_plugin/test/goldens/messageGenerator.pb.dart.meta
@@ -18,8 +18,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: 
-  begin: 1854
-  end: 1860
+  begin: 1827
+  end: 1833
 }
 annotation: {
   path: 4
@@ -27,8 +27,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: 
-  begin: 1902
-  end: 1908
+  begin: 1875
+  end: 1881
 }
 annotation: {
   path: 4
@@ -36,8 +36,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: 
-  begin: 1988
-  end: 1997
+  begin: 1961
+  end: 1970
 }
 annotation: {
   path: 4
@@ -45,8 +45,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: 
-  begin: 2040
-  end: 2051
+  begin: 2013
+  end: 2024
 }
 annotation: {
   path: 4
@@ -54,8 +54,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: 
-  begin: 2123
-  end: 2127
+  begin: 2096
+  end: 2100
 }
 annotation: {
   path: 4
@@ -63,8 +63,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: 
-  begin: 2168
-  end: 2172
+  begin: 2141
+  end: 2145
 }
 annotation: {
   path: 4
@@ -72,8 +72,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: 
-  begin: 2260
-  end: 2267
+  begin: 2233
+  end: 2240
 }
 annotation: {
   path: 4
@@ -81,8 +81,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: 
-  begin: 2310
-  end: 2319
+  begin: 2283
+  end: 2292
 }
 annotation: {
   path: 4
@@ -90,8 +90,8 @@ annotation: {
   path: 2
   path: 2
   sourceFile: 
-  begin: 2382
-  end: 2386
+  begin: 2355
+  end: 2359
 }
 annotation: {
   path: 4
@@ -99,8 +99,8 @@ annotation: {
   path: 2
   path: 2
   sourceFile: 
-  begin: 2433
-  end: 2437
+  begin: 2406
+  end: 2410
 }
 annotation: {
   path: 4
@@ -108,8 +108,8 @@ annotation: {
   path: 2
   path: 2
   sourceFile: 
-  begin: 2517
-  end: 2524
+  begin: 2490
+  end: 2497
 }
 annotation: {
   path: 4
@@ -117,8 +117,8 @@ annotation: {
   path: 2
   path: 2
   sourceFile: 
-  begin: 2567
-  end: 2576
+  begin: 2540
+  end: 2549
 }
 annotation: {
   path: 4
@@ -126,8 +126,8 @@ annotation: {
   path: 2
   path: 3
   sourceFile: 
-  begin: 2688
-  end: 2703
+  begin: 2661
+  end: 2676
 }
 annotation: {
   path: 4
@@ -135,8 +135,8 @@ annotation: {
   path: 2
   path: 3
   sourceFile: 
-  begin: 2794
-  end: 2809
+  begin: 2767
+  end: 2782
 }
 annotation: {
   path: 4
@@ -144,8 +144,8 @@ annotation: {
   path: 2
   path: 3
   sourceFile: 
-  begin: 2938
-  end: 2956
+  begin: 2911
+  end: 2929
 }
 annotation: {
   path: 4
@@ -153,6 +153,6 @@ annotation: {
   path: 2
   path: 3
   sourceFile: 
-  begin: 3048
-  end: 3068
+  begin: 3021
+  end: 3041
 }
diff --git a/protoc_plugin/test/goldens/oneMessage.pb.dart b/protoc_plugin/test/goldens/oneMessage.pb.dart
index e38712f8..59982037 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb.dart
+++ b/protoc_plugin/test/goldens/oneMessage.pb.dart
@@ -37,7 +37,7 @@ class PhoneNumber extends $pb.GeneratedMessage {
         defaultOrMaker: '\$');
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  PhoneNumber clone() => PhoneNumber()..mergeFromMessage(this);
+  PhoneNumber clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   PhoneNumber copyWith(void Function(PhoneNumber) updates) =>
       super.copyWith((message) => updates(message as PhoneNumber))
diff --git a/protoc_plugin/test/goldens/oneMessage.pb.dart.meta b/protoc_plugin/test/goldens/oneMessage.pb.dart.meta
index 44ec15bf..93f21528 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb.dart.meta
+++ b/protoc_plugin/test/goldens/oneMessage.pb.dart.meta
@@ -18,8 +18,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: test
-  begin: 2218
-  end: 2224
+  begin: 2191
+  end: 2197
 }
 annotation: {
   path: 4
@@ -27,8 +27,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: test
-  begin: 2266
-  end: 2272
+  begin: 2239
+  end: 2245
 }
 annotation: {
   path: 4
@@ -36,8 +36,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: test
-  begin: 2352
-  end: 2361
+  begin: 2325
+  end: 2334
 }
 annotation: {
   path: 4
@@ -45,8 +45,8 @@ annotation: {
   path: 2
   path: 0
   sourceFile: test
-  begin: 2404
-  end: 2415
+  begin: 2377
+  end: 2388
 }
 annotation: {
   path: 4
@@ -54,8 +54,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: test
-  begin: 2475
-  end: 2479
+  begin: 2448
+  end: 2452
 }
 annotation: {
   path: 4
@@ -63,8 +63,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: test
-  begin: 2521
-  end: 2525
+  begin: 2494
+  end: 2498
 }
 annotation: {
   path: 4
@@ -72,8 +72,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: test
-  begin: 2607
-  end: 2614
+  begin: 2580
+  end: 2587
 }
 annotation: {
   path: 4
@@ -81,8 +81,8 @@ annotation: {
   path: 2
   path: 1
   sourceFile: test
-  begin: 2657
-  end: 2666
+  begin: 2630
+  end: 2639
 }
 annotation: {
   path: 4
@@ -90,8 +90,8 @@ annotation: {
   path: 2
   path: 2
   sourceFile: test
-  begin: 2729
-  end: 2733
+  begin: 2702
+  end: 2706
 }
 annotation: {
   path: 4
@@ -99,8 +99,8 @@ annotation: {
   path: 2
   path: 2
   sourceFile: test
-  begin: 2780
-  end: 2784
+  begin: 2753
+  end: 2757
 }
 annotation: {
   path: 4
@@ -108,8 +108,8 @@ annotation: {
   path: 2
   path: 2
   sourceFile: test
-  begin: 2864
-  end: 2871
+  begin: 2837
+  end: 2844
 }
 annotation: {
   path: 4
@@ -117,6 +117,6 @@ annotation: {
   path: 2
   path: 2
   sourceFile: test
-  begin: 2914
-  end: 2923
+  begin: 2887
+  end: 2896
 }
diff --git a/protoc_plugin/test/goldens/service.pb.dart b/protoc_plugin/test/goldens/service.pb.dart
index fda76ba7..d9c6c0a8 100644
--- a/protoc_plugin/test/goldens/service.pb.dart
+++ b/protoc_plugin/test/goldens/service.pb.dart
@@ -35,7 +35,7 @@ class Empty extends $pb.GeneratedMessage {
     ..hasRequiredFields = false;
 
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
-  Empty clone() => Empty()..mergeFromMessage(this);
+  Empty clone() => deepCopy();
   @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
   Empty copyWith(void Function(Empty) updates) =>
       super.copyWith((message) => updates(message as Empty)) as Empty;
diff --git a/protoc_plugin/test/oneof_test.dart b/protoc_plugin/test/oneof_test.dart
index a99cc31f..26179164 100644
--- a/protoc_plugin/test/oneof_test.dart
+++ b/protoc_plugin/test/oneof_test.dart
@@ -174,15 +174,11 @@ void main() {
   test('copyWith preserves oneof state', () {
     final foo = Foo();
     expectOneofNotSet(foo);
-    // `ignore` below to work around https://github.com/dart-lang/sdk/issues/48879
-    final copy1 =
-        foo.deepCopy().freeze().rebuild((_) {}) as Foo; // ignore: unused_result
+    final copy1 = foo.deepCopy().freeze().rebuild((_) {}) as Foo;
     expectOneofNotSet(copy1);
     foo.first = 'oneof';
     expectFirstSet(foo);
-    // `ignore` below to work around https://github.com/dart-lang/sdk/issues/48879
-    final copy2 =
-        foo.deepCopy().freeze().rebuild((_) {}) as Foo; // ignore: unused_result
+    final copy2 = foo.deepCopy();
     expectFirstSet(copy2);
   });