named optional arguments in constructors for messages (#441)
diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md
index cfa93cf..29a7741 100644
--- a/protoc_plugin/CHANGELOG.md
+++ b/protoc_plugin/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 20.0.0-nullsafety.1
+
+* Generate constructors with optional named arguments for prefilling fields.
## 20.0.0-nullsafety.0
* Generate null-safe code.
diff --git a/protoc_plugin/lib/base_type.dart b/protoc_plugin/lib/base_type.dart
index 7559c7e..8bb6d8f 100644
--- a/protoc_plugin/lib/base_type.dart
+++ b/protoc_plugin/lib/base_type.dart
@@ -55,6 +55,9 @@
String getRepeatedDartType(FileGenerator fileGen) =>
"$_coreImportPrefix.List<${getDartType(fileGen)}>";
+ String getRepeatedDartTypeIterable(FileGenerator fileGen) =>
+ "$_coreImportPrefix.Iterable<${getDartType(fileGen)}>";
+
factory BaseType(FieldDescriptorProto field, GenerationContext ctx) {
String constSuffix;
diff --git a/protoc_plugin/lib/message_generator.dart b/protoc_plugin/lib/message_generator.dart
index 227eb78..983b6b1 100644
--- a/protoc_plugin/lib/message_generator.dart
+++ b/protoc_plugin/lib/message_generator.dart
@@ -357,7 +357,44 @@
out.printlnAnnotated('${classname}._() : super();', [
NamedLocation(name: classname, fieldPathSegment: fieldPath, start: 0)
]);
- out.println('factory ${classname}() => create();');
+ out.print('factory $classname(');
+ if (_fieldList.isNotEmpty) {
+ out.println('{');
+ for (final field in _fieldList) {
+ _emitDeprecatedIf(field.isDeprecated, out);
+ if (field.isRepeated && !field.isMapField) {
+ out.println(
+ ' ${field.baseType.getRepeatedDartTypeIterable(fileGen)}? ${field.memberNames.fieldName},');
+ } else {
+ out.println(
+ ' ${field.getDartType(fileGen)}? ${field.memberNames.fieldName},');
+ }
+ }
+ out.print('}');
+ }
+ if (_fieldList.isNotEmpty) {
+ out.println(') {');
+ out.println(' final _result = create();');
+ for (final field in _fieldList) {
+ out.println(' if (${field.memberNames.fieldName} != null) {');
+ if (field.isDeprecated) {
+ out.println(
+ ' // ignore: deprecated_member_use_from_same_package');
+ }
+ if (field.isRepeated || field.isMapField) {
+ out.println(
+ ' _result.${field.memberNames.fieldName}.addAll(${field.memberNames.fieldName});');
+ } else {
+ out.println(
+ ' _result.${field.memberNames.fieldName} = ${field.memberNames.fieldName};');
+ }
+ out.println(' }');
+ }
+ out.println(' return _result;');
+ out.println('}');
+ } else {
+ out.println(') => create();');
+ }
out.println(
'factory ${classname}.fromBuffer($_coreImportPrefix.List<$_coreImportPrefix.int> i,'
' [$_protobufImportPrefix.ExtensionRegistry r = $_protobufImportPrefix.ExtensionRegistry.EMPTY])'
diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml
index 37f8e6d..114327e 100644
--- a/protoc_plugin/pubspec.yaml
+++ b/protoc_plugin/pubspec.yaml
@@ -1,5 +1,5 @@
name: protoc_plugin
-version: 20.0.0-nullsafety.0
+version: 20.0.0-nullsafety.1
description: Protoc compiler plugin to generate Dart code
homepage: https://github.com/dart-lang/protobuf
diff --git a/protoc_plugin/test/generated_message_test.dart b/protoc_plugin/test/generated_message_test.dart
index 34359cb..461fd8c 100755
--- a/protoc_plugin/test/generated_message_test.dart
+++ b/protoc_plugin/test/generated_message_test.dart
@@ -887,4 +887,88 @@
final value2 = value1.deepCopy();
assertAllExtensionsSet(value2);
});
+
+ test('named arguments in constructor', () {
+ final value = TestAllTypes(
+ optionalInt32: 101,
+ optionalInt64: make64(102),
+ optionalUint32: 103,
+ optionalUint64: make64(104),
+ optionalSint32: 105,
+ optionalSint64: make64(106),
+ optionalFixed32: 107,
+ optionalFixed64: make64(108),
+ optionalSfixed32: 109,
+ optionalSfixed64: make64(110),
+ optionalFloat: 111.0,
+ optionalDouble: 112.0,
+ optionalBool: true,
+ optionalString: '115',
+ optionalBytes: '116'.codeUnits,
+ optionalGroup: TestAllTypes_OptionalGroup(a: 117),
+ optionalNestedMessage: TestAllTypes_NestedMessage(bb: 118),
+ optionalForeignMessage: ForeignMessage(c: 119),
+ optionalImportMessage: ImportMessage(d: 120),
+ optionalNestedEnum: TestAllTypes_NestedEnum.BAZ,
+ optionalForeignEnum: ForeignEnum.FOREIGN_BAZ,
+ optionalImportEnum: ImportEnum.IMPORT_BAZ,
+ optionalStringPiece: '124',
+ optionalCord: '125',
+ repeatedInt32: [201, 301],
+ repeatedInt64: [make64(202), make64(302)],
+ repeatedUint32: [203, 303],
+ repeatedUint64: [make64(204), make64(304)],
+ repeatedSint32: [205, 305],
+ repeatedSint64: [make64(206), make64(306)],
+ repeatedFixed32: [207, 307],
+ repeatedFixed64: [make64(208), make64(308)],
+ repeatedSfixed32: [209, 309],
+ repeatedSfixed64: [make64(210), make64(310)],
+ repeatedFloat: [211.0, 311.0],
+ repeatedDouble: [212.0, 312.0],
+ repeatedBool: [true, false],
+ repeatedString: ['215', '315'],
+ repeatedBytes: ['216'.codeUnits, '316'.codeUnits],
+ repeatedGroup: [
+ TestAllTypes_RepeatedGroup(a: 217),
+ TestAllTypes_RepeatedGroup(a: 317)
+ ],
+ repeatedNestedMessage: [
+ TestAllTypes_NestedMessage(bb: 218),
+ TestAllTypes_NestedMessage(bb: 318)
+ ],
+ repeatedForeignMessage: [ForeignMessage(c: 219), ForeignMessage(c: 319)],
+ repeatedImportMessage: [ImportMessage(d: 220), ImportMessage(d: 320)],
+ repeatedNestedEnum: [
+ TestAllTypes_NestedEnum.BAR,
+ TestAllTypes_NestedEnum.BAZ
+ ],
+ repeatedForeignEnum: [ForeignEnum.FOREIGN_BAR, ForeignEnum.FOREIGN_BAZ],
+ repeatedImportEnum: [ImportEnum.IMPORT_BAR, ImportEnum.IMPORT_BAZ],
+ repeatedStringPiece: ['224', '324'],
+ repeatedCord: ['225', '325'],
+ defaultInt32: 401,
+ defaultInt64: make64(402),
+ defaultUint32: 403,
+ defaultUint64: make64(404),
+ defaultSint32: 405,
+ defaultSint64: make64(406),
+ defaultFixed32: 407,
+ defaultFixed64: make64(408),
+ defaultSfixed32: 409,
+ defaultSfixed64: make64(410),
+ defaultFloat: 411.0,
+ defaultDouble: 412.0,
+ defaultBool: false,
+ defaultString: '415',
+ defaultBytes: '416'.codeUnits,
+ defaultNestedEnum: TestAllTypes_NestedEnum.FOO,
+ defaultForeignEnum: ForeignEnum.FOREIGN_FOO,
+ defaultImportEnum: ImportEnum.IMPORT_FOO,
+ defaultStringPiece: '424',
+ defaultCord: '425',
+ );
+
+ assertAllFieldsSet(value);
+ });
}
diff --git a/protoc_plugin/test/goldens/imports.pb b/protoc_plugin/test/goldens/imports.pb
index 54f84fb..52a4166 100644
--- a/protoc_plugin/test/goldens/imports.pb
+++ b/protoc_plugin/test/goldens/imports.pb
@@ -21,7 +21,23 @@
;
M._() : super();
- factory M() => create();
+ factory M({
+ M? m,
+ $1.M? m1,
+ $2.M? m2,
+ }) {
+ final _result = create();
+ if (m != null) {
+ _result.m = m;
+ }
+ if (m1 != null) {
+ _result.m1 = m1;
+ }
+ if (m2 != null) {
+ _result.m2 = m2;
+ }
+ return _result;
+ }
factory M.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory M.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
diff --git a/protoc_plugin/test/goldens/int64.pb b/protoc_plugin/test/goldens/int64.pb
index 70782e6..922414b 100644
--- a/protoc_plugin/test/goldens/int64.pb
+++ b/protoc_plugin/test/goldens/int64.pb
@@ -17,7 +17,15 @@
;
Int64._() : super();
- factory Int64() => create();
+ factory Int64({
+ $fixnum.Int64? value,
+ }) {
+ final _result = create();
+ if (value != null) {
+ _result.value = value;
+ }
+ return _result;
+ }
factory Int64.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory Int64.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
diff --git a/protoc_plugin/test/goldens/messageGenerator b/protoc_plugin/test/goldens/messageGenerator
index 6840a22..151559c 100644
--- a/protoc_plugin/test/goldens/messageGenerator
+++ b/protoc_plugin/test/goldens/messageGenerator
@@ -7,7 +7,29 @@
;
PhoneNumber._() : super();
- factory PhoneNumber() => create();
+ factory PhoneNumber({
+ $core.String? number,
+ PhoneNumber_PhoneType? type,
+ $core.String? name,
+ @$core.Deprecated('This field is deprecated.')
+ $core.String? deprecatedField,
+ }) {
+ final _result = create();
+ if (number != null) {
+ _result.number = number;
+ }
+ if (type != null) {
+ _result.type = type;
+ }
+ if (name != null) {
+ _result.name = name;
+ }
+ if (deprecatedField != null) {
+ // ignore: deprecated_member_use_from_same_package
+ _result.deprecatedField = deprecatedField;
+ }
+ return _result;
+ }
factory PhoneNumber.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory PhoneNumber.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
diff --git a/protoc_plugin/test/goldens/messageGenerator.meta b/protoc_plugin/test/goldens/messageGenerator.meta
index e852feb..2fd6f55 100644
--- a/protoc_plugin/test/goldens/messageGenerator.meta
+++ b/protoc_plugin/test/goldens/messageGenerator.meta
@@ -18,8 +18,8 @@
path: 2
path: 1
sourceFile:
- begin: 2263
- end: 2269
+ begin: 2802
+ end: 2808
}
annotation: {
path: 4
@@ -27,8 +27,8 @@
path: 2
path: 1
sourceFile:
- begin: 2311
- end: 2317
+ begin: 2850
+ end: 2856
}
annotation: {
path: 4
@@ -36,8 +36,8 @@
path: 2
path: 1
sourceFile:
- begin: 2390
- end: 2399
+ begin: 2929
+ end: 2938
}
annotation: {
path: 4
@@ -45,8 +45,8 @@
path: 2
path: 1
sourceFile:
- begin: 2442
- end: 2453
+ begin: 2981
+ end: 2992
}
annotation: {
path: 4
@@ -54,8 +54,8 @@
path: 2
path: 0
sourceFile:
- begin: 2523
- end: 2527
+ begin: 3062
+ end: 3066
}
annotation: {
path: 4
@@ -63,8 +63,8 @@
path: 2
path: 0
sourceFile:
- begin: 2568
- end: 2572
+ begin: 3107
+ end: 3111
}
annotation: {
path: 4
@@ -72,8 +72,8 @@
path: 2
path: 0
sourceFile:
- begin: 2651
- end: 2658
+ begin: 3190
+ end: 3197
}
annotation: {
path: 4
@@ -81,8 +81,8 @@
path: 2
path: 0
sourceFile:
- begin: 2701
- end: 2710
+ begin: 3240
+ end: 3249
}
annotation: {
path: 4
@@ -90,8 +90,8 @@
path: 2
path: 2
sourceFile:
- begin: 2771
- end: 2775
+ begin: 3310
+ end: 3314
}
annotation: {
path: 4
@@ -99,8 +99,8 @@
path: 2
path: 2
sourceFile:
- begin: 2822
- end: 2826
+ begin: 3361
+ end: 3365
}
annotation: {
path: 4
@@ -108,8 +108,8 @@
path: 2
path: 2
sourceFile:
- begin: 2899
- end: 2906
+ begin: 3438
+ end: 3445
}
annotation: {
path: 4
@@ -117,8 +117,8 @@
path: 2
path: 2
sourceFile:
- begin: 2949
- end: 2958
+ begin: 3488
+ end: 3497
}
annotation: {
path: 4
@@ -126,8 +126,8 @@
path: 2
path: 3
sourceFile:
- begin: 3068
- end: 3083
+ begin: 3607
+ end: 3622
}
annotation: {
path: 4
@@ -135,8 +135,8 @@
path: 2
path: 3
sourceFile:
- begin: 3174
- end: 3189
+ begin: 3713
+ end: 3728
}
annotation: {
path: 4
@@ -144,8 +144,8 @@
path: 2
path: 3
sourceFile:
- begin: 3311
- end: 3329
+ begin: 3850
+ end: 3868
}
annotation: {
path: 4
@@ -153,6 +153,6 @@
path: 2
path: 3
sourceFile:
- begin: 3421
- end: 3441
+ begin: 3960
+ end: 3980
}
diff --git a/protoc_plugin/test/goldens/oneMessage.pb b/protoc_plugin/test/goldens/oneMessage.pb
index dbe5523..f07c350 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb
+++ b/protoc_plugin/test/goldens/oneMessage.pb
@@ -17,7 +17,23 @@
;
PhoneNumber._() : super();
- factory PhoneNumber() => create();
+ factory PhoneNumber({
+ $core.String? number,
+ $core.int? type,
+ $core.String? name,
+ }) {
+ final _result = create();
+ if (number != null) {
+ _result.number = number;
+ }
+ if (type != null) {
+ _result.type = type;
+ }
+ if (name != null) {
+ _result.name = name;
+ }
+ return _result;
+ }
factory PhoneNumber.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory PhoneNumber.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
diff --git a/protoc_plugin/test/goldens/oneMessage.pb.meta b/protoc_plugin/test/goldens/oneMessage.pb.meta
index 70e4ab4..72f03dc 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb.meta
+++ b/protoc_plugin/test/goldens/oneMessage.pb.meta
@@ -18,8 +18,8 @@
path: 2
path: 0
sourceFile: test
- begin: 2390
- end: 2396
+ begin: 2686
+ end: 2692
}
annotation: {
path: 4
@@ -27,8 +27,8 @@
path: 2
path: 0
sourceFile: test
- begin: 2438
- end: 2444
+ begin: 2734
+ end: 2740
}
annotation: {
path: 4
@@ -36,51 +36,6 @@
path: 2
path: 0
sourceFile: test
- begin: 2517
- end: 2526
-}
-annotation: {
- path: 4
- path: 0
- path: 2
- path: 0
- sourceFile: test
- begin: 2569
- end: 2580
-}
-annotation: {
- path: 4
- path: 0
- path: 2
- path: 1
- sourceFile: test
- begin: 2638
- end: 2642
-}
-annotation: {
- path: 4
- path: 0
- path: 2
- path: 1
- sourceFile: test
- begin: 2684
- end: 2688
-}
-annotation: {
- path: 4
- path: 0
- path: 2
- path: 1
- sourceFile: test
- begin: 2763
- end: 2770
-}
-annotation: {
- path: 4
- path: 0
- path: 2
- path: 1
- sourceFile: test
begin: 2813
end: 2822
}
@@ -88,16 +43,16 @@
path: 4
path: 0
path: 2
- path: 2
+ path: 0
sourceFile: test
- begin: 2883
- end: 2887
+ begin: 2865
+ end: 2876
}
annotation: {
path: 4
path: 0
path: 2
- path: 2
+ path: 1
sourceFile: test
begin: 2934
end: 2938
@@ -106,10 +61,28 @@
path: 4
path: 0
path: 2
- path: 2
+ path: 1
sourceFile: test
- begin: 3011
- end: 3018
+ begin: 2980
+ end: 2984
+}
+annotation: {
+ path: 4
+ path: 0
+ path: 2
+ path: 1
+ sourceFile: test
+ begin: 3059
+ end: 3066
+}
+annotation: {
+ path: 4
+ path: 0
+ path: 2
+ path: 1
+ sourceFile: test
+ begin: 3109
+ end: 3118
}
annotation: {
path: 4
@@ -117,6 +90,33 @@
path: 2
path: 2
sourceFile: test
- begin: 3061
- end: 3070
+ begin: 3179
+ end: 3183
+}
+annotation: {
+ path: 4
+ path: 0
+ path: 2
+ path: 2
+ sourceFile: test
+ begin: 3230
+ end: 3234
+}
+annotation: {
+ path: 4
+ path: 0
+ path: 2
+ path: 2
+ sourceFile: test
+ begin: 3307
+ end: 3314
+}
+annotation: {
+ path: 4
+ path: 0
+ path: 2
+ path: 2
+ sourceFile: test
+ begin: 3357
+ end: 3366
}
diff --git a/protoc_plugin/test/map_field_test.dart b/protoc_plugin/test/map_field_test.dart
index ff94157..ec17f35 100644
--- a/protoc_plugin/test/map_field_test.dart
+++ b/protoc_plugin/test/map_field_test.dart
@@ -341,4 +341,28 @@
final value = testMap.getField(mapFieldInfo.tagNumber);
expect(value is Map<int, List<int>>, true);
});
+
+ test('named optional arguments in cosntructor', () {
+ final testMap = TestMap(
+ int32ToInt32Field: {1: 11, 2: 22, 3: 33},
+ int32ToStringField: {1: '11', 2: '22', 3: '33'},
+ int32ToBytesField: {
+ 1: utf8.encode('11'),
+ 2: utf8.encode('22'),
+ 3: utf8.encode('33')
+ },
+ int32ToEnumField: {
+ 1: TestMap_EnumValue.DEFAULT,
+ 2: TestMap_EnumValue.BAR,
+ 3: TestMap_EnumValue.BAZ
+ },
+ int32ToMessageField: {
+ 1: TestMap_MessageValue(value: 11),
+ 2: TestMap_MessageValue(value: 22),
+ 3: TestMap_MessageValue(value: 33)
+ },
+ stringToInt32Field: {'1': 11, '2': 22, '3': 33},
+ );
+ _expectMapValuesSet(testMap);
+ });
}