Merge changes from internal repo. (#70)
* Ensure generated extension class names don't conflict with message
class names.
* Function will soon be a reserved keyword, so don't generate classes
with that name.
* Strong mode tweaks.
diff --git a/Makefile b/Makefile
index bd370d4..77713a9 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@
dart_options \
descriptor_2_5_opensource \
enum_extension \
+ ExtensionNameConflict \
map_api \
map_api2 \
mixins \
diff --git a/lib/file_generator.dart b/lib/file_generator.dart
index a424744..631d35d 100644
--- a/lib/file_generator.dart
+++ b/lib/file_generator.dart
@@ -166,11 +166,6 @@
return index == -1 ? fileName : fileName.substring(0, index);
}
- String _generateClassName(Uri protoFilePath) {
- String s = _fileNameWithoutExtension(protoFilePath).replaceAll('-', '_');
- return '${s[0].toUpperCase()}${s.substring(1)}';
- }
-
/// Generates all the Dart files for this .proto file.
List<CodeGeneratorResponse_File> generateFiles(OutputConfiguration config) {
if (!_linked) throw new StateError("not linked");
@@ -208,7 +203,7 @@
// name derived from the file name.
if (!extensionGenerators.isEmpty) {
// TODO(antonm): do not generate a class.
- String className = _generateClassName(protoFileUri);
+ String className = extensionClassName(descriptor);
out.addBlock('class $className {', '}\n', () {
for (ExtensionGenerator x in extensionGenerators) {
x.generate(out);
@@ -233,6 +228,11 @@
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
_writeLibraryHeading(out);
+ // Make sure any other symbols in dart:core don't cause name conflicts with
+ // protobuf classes that have the same name.
+ out.println("// ignore: UNUSED_SHOWN_NAME\n"
+ "import 'dart:core' show int, bool, double, String, List, override;");
+
// We only add the dart:async import if there are services in the
// FileDescriptorProto.
if (descriptor.service.isNotEmpty) {
@@ -347,6 +347,10 @@
_writeLibraryHeading(out, "pbenum");
if (enumCount > 0) {
+ // Make sure any other symbols in dart:core don't cause name conflicts
+ // with enums that have the same name.
+ out.println("// ignore: UNUSED_SHOWN_NAME\n"
+ "import 'dart:core' show int, dynamic, String, List, Map;");
out.println("import 'package:protobuf/protobuf.dart';");
out.println();
}
diff --git a/lib/message_generator.dart b/lib/message_generator.dart
index 7e1d2c1..9531dbc 100644
--- a/lib/message_generator.dart
+++ b/lib/message_generator.dart
@@ -24,8 +24,14 @@
return mixin;
}
+ /// The name of the Dart class to generate.
final String classname;
+
+ /// The fully-qualified name of the message type.
+ ///
+ /// (Used as a unique key and in error messages, not in Dart code.)
final String fqname;
+
final PbMixin mixin;
final ProtobufContainer _parent;
@@ -41,9 +47,7 @@
Map<String, PbMixin> declaredMixins, PbMixin defaultMixin)
: _descriptor = descriptor,
_parent = parent,
- classname = (parent.classname == '')
- ? descriptor.name
- : '${parent.classname}_${descriptor.name}',
+ classname = messageClassName(descriptor, parent: parent.classname),
fqname = (parent == null || parent.fqname == null)
? descriptor.name
: (parent.fqname == '.'
diff --git a/lib/names.dart b/lib/names.dart
index b73665f..b25b03c 100644
--- a/lib/names.dart
+++ b/lib/names.dart
@@ -49,6 +49,41 @@
return _unusedMemberNames(descriptor, null, existingNames).fieldName;
}
+/// Chooses the name of the Dart class holding top-level extensions.
+String extensionClassName(FileDescriptorProto descriptor) {
+ var taken = new Set<String>();
+ for (var messageType in descriptor.messageType) {
+ taken.add(messageClassName(messageType));
+ }
+
+ String s = _fileNameWithoutExtension(descriptor).replaceAll('-', '_');
+ String candidate = '${s[0].toUpperCase()}${s.substring(1)}';
+
+ if (!taken.contains(candidate)) {
+ return candidate;
+ }
+
+ // Found a conflict; try again.
+ candidate = "${candidate}Ext";
+ if (!taken.contains(candidate)) {
+ return candidate;
+ }
+
+ // Next, try numbers.
+ int suffix = 2;
+ while (taken.contains("$candidate$suffix")) {
+ suffix++;
+ }
+ return "$candidate$suffix";
+}
+
+String _fileNameWithoutExtension(FileDescriptorProto descriptor) {
+ Uri path = new Uri.file(descriptor.name);
+ String fileName = path.pathSegments.last;
+ int dot = fileName.lastIndexOf(".");
+ return dot == -1 ? fileName : fileName.substring(0, dot);
+}
+
// Exception thrown when a field has an invalid 'dart_name' option.
class DartNameOptionException implements Exception {
final String message;
@@ -56,6 +91,24 @@
String toString() => "$message";
}
+/// Chooses the name of the Dart class to generate for a protobuf message.
+///
+/// For a nested message, [parent] should be provided
+/// with the name of the Dart class for the immediate parent.
+String messageClassName(DescriptorProto descriptor, {String parent: ''}) {
+ var name = descriptor.name;
+ if (parent != '') {
+ name = '${parent}_${descriptor.name}';
+ }
+ if (name == 'Function') {
+ name = 'Function_'; // Avoid reserved word.
+ } else if (name.startsWith('Function_')) {
+ // Avoid any further name conflicts due to 'Function' rename (unlikely).
+ name = name + '_';
+ }
+ return name;
+}
+
/// Chooses the GeneratedMessage member names for each field.
///
/// Additional names to avoid can be supplied using [reserved].
diff --git a/lib/service_generator.dart b/lib/service_generator.dart
index e061240..75d8e0c 100644
--- a/lib/service_generator.dart
+++ b/lib/service_generator.dart
@@ -177,7 +177,7 @@
}
/// Hook for generating members added in subclasses.
- void _generateMoreClassMembers(out) {}
+ void _generateMoreClassMembers(IndentingWriter out) {}
void generate(IndentingWriter out) {
out.addBlock(
@@ -189,7 +189,7 @@
_generateDispatchMethod(out);
_generateMoreClassMembers(out);
out.println("Map<String, dynamic> get \$json => $jsonConstant;");
- out.println("Map<String, dynamic> get \$messageJson =>"
+ out.println("Map<String, Map<String, dynamic>> get \$messageJson =>"
" $messageJsonConstant;");
});
out.println();
diff --git a/pubspec.yaml b/pubspec.yaml
index 4acb45c..217bfad 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: protoc_plugin
-version: 0.7.0-dev
+version: 0.7.1
author: Dart Team <misc@dartlang.org>
description: Protoc compiler plugin to generate Dart code
homepage: https://github.com/dart-lang/dart-protoc-plugin
@@ -8,7 +8,7 @@
dependencies:
fixnum: ^0.10.5
path: ^1.0.0
- protobuf: ^0.5.2
+ protobuf: ^0.5.4
dev_dependencies:
browser: any
test: ^0.12.0
diff --git a/test/all_tests.dart b/test/all_tests.dart
index 3e6cbf9..e44667e 100755
--- a/test/all_tests.dart
+++ b/test/all_tests.dart
@@ -19,6 +19,7 @@
import 'message_generator_test.dart' as message_generator;
import 'message_test.dart' as message;
import 'mixin_test.dart' as mixin;
+import 'names_test.dart' as names;
import 'protoc_options_test.dart' as protoc_options;
import 'repeated_field_test.dart' as repeated_field;
import 'service_test.dart' as service;
@@ -42,6 +43,7 @@
message_generator.main();
message.main();
mixin.main();
+ names.main();
protoc_options.main();
repeated_field.main();
service.main();
diff --git a/test/extension_test.dart b/test/extension_test.dart
index fb56da7..8829733 100644
--- a/test/extension_test.dart
+++ b/test/extension_test.dart
@@ -11,6 +11,7 @@
import '../out/protos/enum_extension.pb.dart';
import '../out/protos/nested_extension.pb.dart';
import '../out/protos/non_nested_extension.pb.dart';
+import '../out/protos/ExtensionNameConflict.pb.dart';
import 'test_util.dart';
diff --git a/test/file_generator_test.dart b/test/file_generator_test.dart
index 0a9dbfd..50eb849 100644
--- a/test/file_generator_test.dart
+++ b/test/file_generator_test.dart
@@ -78,6 +78,8 @@
///
library test;
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
import 'package:protobuf/protobuf.dart';
class PhoneNumber extends GeneratedMessage {
@@ -165,6 +167,8 @@
///
library test;
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
export 'test.pbenum.dart';
''';
@@ -175,6 +179,8 @@
///
library test_pbenum;
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, dynamic, String, List, Map;
import 'package:protobuf/protobuf.dart';
class PhoneType extends ProtobufEnum {
@@ -248,6 +254,8 @@
///
library pb_library_test;
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
import 'package:protobuf/protobuf.dart';
''';
@@ -271,6 +279,8 @@
///
library test;
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
import 'package:fixnum/fixnum.dart';
import 'package:protobuf/protobuf.dart';
@@ -304,6 +314,8 @@
///
library test;
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
import 'dart:async';
import 'package:protobuf/protobuf.dart';
@@ -377,7 +389,7 @@
}
Map<String, dynamic> get $json => Test$json;
- Map<String, dynamic> get $messageJson => Test$messageJson;
+ Map<String, Map<String, dynamic>> get $messageJson => Test$messageJson;
}
''';
@@ -416,6 +428,8 @@
///
library test;
+// ignore: UNUSED_SHOWN_NAME
+import 'dart:core' show int, bool, double, String, List, override;
import 'package:protobuf/protobuf.dart';
import 'package1.pb.dart' as p1;
diff --git a/test/indenting_writer_test.dart b/test/indenting_writer_test.dart
index 5bbfeba..61f0427 100755
--- a/test/indenting_writer_test.dart
+++ b/test/indenting_writer_test.dart
@@ -17,9 +17,7 @@
out.println('second;');
});
- expect(
- out.toString(),
- '''
+ expect(out.toString(), '''
class test {
first;
diff --git a/test/json_test.dart b/test/json_test.dart
index 155421f..b698eac 100755
--- a/test/json_test.dart
+++ b/test/json_test.dart
@@ -13,20 +13,20 @@
import 'test_util.dart';
void main() {
- final String TEST_ALL_TYPES_JSON = '{"1":101,"2":102,"3":103,"4":104,'
- '"5":105,"6":106,"7":107,"8":108,"9":109,"10":110,"11":111.0,'
+ final String TEST_ALL_TYPES_JSON = '{"1":101,"2":"102","3":103,"4":"104",'
+ '"5":105,"6":"106","7":107,"8":"108","9":109,"10":"110","11":111.0,'
'"12":112.0,"13":true,"14":"115","15":"MTE2","16":{"17":117},'
'"18":{"1":118},"19":{"1":119},"20":{"1":120},"21":3,"22":6,"23":9,'
- '"24":"124","25":"125","31":[201,301],"32":[202,302],'
- '"33":[203,303],"34":[204,304],"35":[205,305],"36":[206,306],'
- '"37":[207,307],"38":[208,308],"39":[209,309],"40":[210,310],'
+ '"24":"124","25":"125","31":[201,301],"32":["202","302"],'
+ '"33":[203,303],"34":["204","304"],"35":[205,305],"36":["206","306"],'
+ '"37":[207,307],"38":["208","308"],"39":[209,309],"40":["210","310"],'
'"41":[211.0,311.0],"42":[212.0,312.0],"43":[true,false],'
'"44":["215","315"],"45":["MjE2","MzE2"],"46":[{"47":217},{"47":317}],'
'"48":[{"1":218},{"1":318}],"49":[{"1":219},{"1":319}],'
'"50":[{"1":220},{"1":320}],"51":[2,3],"52":[5,6],"53":[8,9],'
- '"54":["224","324"],"55":["225","325"],"61":401,"62":402,"63":403,'
- '"64":404,"65":405,"66":406,"67":407,"68":408,"69":409,'
- '"70":410,"71":411.0,"72":412.0,"73":false,"74":"415","75":"NDE2",'
+ '"54":["224","324"],"55":["225","325"],"61":401,"62":"402","63":403,'
+ '"64":"404","65":405,"66":"406","67":407,"68":"408","69":409,'
+ '"70":"410","71":411.0,"72":412.0,"73":false,"74":"415","75":"NDE2",'
'"81":1,"82":4,"83":7,"84":"424","85":"425"}';
/**
@@ -50,15 +50,15 @@
expect(getAllSet()..optionalInt32 = -1234567,
expectedJson(':101,', ':-1234567,'));
- // 64-bit numbers outside 53-bit range are quoted.
+ // All 64-bit numbers are quoted.
expect(getAllSet()..optionalInt64 = make64(0, 0x200000),
- expectedJson(':102,', ':9007199254740992,'));
+ expectedJson(':"102",', ':"9007199254740992",'));
expect(getAllSet()..optionalInt64 = make64(1, 0x200000),
- expectedJson(':102,', ':"9007199254740993",'));
+ expectedJson(':"102",', ':"9007199254740993",'));
expect(getAllSet()..optionalInt64 = -make64(0, 0x200000),
- expectedJson(':102,', ':-9007199254740992,'));
+ expectedJson(':"102",', ':"-9007199254740992",'));
expect(getAllSet()..optionalInt64 = -make64(1, 0x200000),
- expectedJson(':102,', ':"-9007199254740993",'));
+ expectedJson(':"102",', ':"-9007199254740993",'));
// Quotes, backslashes, and control characters in strings are quoted.
expect(getAllSet()..optionalString = 'a\u0000b\u0001cd\\e\"fg',
@@ -119,4 +119,28 @@
expect(new TestAllExtensions.fromJson(TEST_ALL_TYPES_JSON, registry),
getAllExtensionsSet());
});
+
+ test('testUnknownEnumValueInOptionalField', () {
+ // optional NestedEnum optional_nested_enum = 21;
+ var message = new TestAllTypes.fromJson('{"21": 4}');
+ // 4 is an unknown value.
+ expect(message.optionalNestedEnum, equals(TestAllTypes_NestedEnum.FOO));
+ });
+
+ test('testUnknownEnumValueInRepeatedField', () {
+ // repeated NestedEnum repeated_nested_enum = 51;
+ var message = new TestAllTypes.fromJson('{"51": [4]}');
+ // 4 is an unknown value.
+ expect(message.repeatedNestedEnum, isEmpty);
+
+ // 1 (FOO) and 2 (BAR) are known values.
+ message = new TestAllTypes.fromJson('{"51": [1, 4, 2, 4, 1, 4]}');
+ expect(
+ message.repeatedNestedEnum,
+ equals([
+ TestAllTypes_NestedEnum.FOO,
+ TestAllTypes_NestedEnum.BAR,
+ TestAllTypes_NestedEnum.FOO
+ ]));
+ });
}
diff --git a/test/names_test.dart b/test/names_test.dart
index 7f31ab8..95906c0 100644
--- a/test/names_test.dart
+++ b/test/names_test.dart
@@ -9,7 +9,7 @@
import 'package:protoc_plugin/src/descriptor.pb.dart';
import 'package:protoc_plugin/src/dart_options.pb.dart';
-import '../out/protos/dart_name.pb.dart';
+import '../out/protos/dart_name.pb.dart' as pb;
Matcher throwsMessage(String msg) => throwsA(new _ToStringMatcher(equals(msg)));
@@ -21,7 +21,7 @@
void main() {
test('Can access a field that was renamed using dart_name option', () {
- var msg = new DartName();
+ var msg = new pb.DartName();
expect(msg.hasRenamedField(), false);
msg.renamedField = 'test';
expect(msg.hasRenamedField(), true);
@@ -31,7 +31,7 @@
});
test('Can swap field names using dart_name option', () {
- var msg = new SwapNames();
+ var msg = new pb.SwapNames();
msg.first = "one";
msg.second = "two";
expect(msg.getField(1), "two");
@@ -39,7 +39,7 @@
});
test("Can take another field's name using dart_name option", () {
- var msg = new TakeExistingName();
+ var msg = new pb.TakeExistingName();
msg.first = "one";
expect(msg.getField(2), "one");
msg.first_1 = "renamed";
@@ -81,6 +81,11 @@
throwsMessage("Example.second: "
"dart_name option is invalid: 'renamed' is already used"));
});
+
+ test('message classes renamed to avoid Function keyword', () {
+ new pb.Function_()..fun = 'renamed';
+ new pb.Function__()..fun1 = 'also renamed';
+ });
}
FieldDescriptorProto stringField(String name, int number, String dartName) {
diff --git a/test/protos/ExtensionNameConflict.proto b/test/protos/ExtensionNameConflict.proto
new file mode 100644
index 0000000..21217eb
--- /dev/null
+++ b/test/protos/ExtensionNameConflict.proto
@@ -0,0 +1,16 @@
+syntax = "proto2";
+
+package protobuf_unittest;
+
+// Cause a conflict with the auto-generated extension class.
+message ExtensionNameConflict {
+}
+
+message AnotherMessageToBeExtended {
+ extensions 1 to max;
+}
+
+extend AnotherMessageToBeExtended {
+ optional int32 someExtension = 1;
+}
+
diff --git a/test/protos/dart_name.proto b/test/protos/dart_name.proto
index 33b157b..533d43c 100644
--- a/test/protos/dart_name.proto
+++ b/test/protos/dart_name.proto
@@ -29,3 +29,11 @@
(dart_options.dart_name) = "first"
];
}
+
+message Function {
+ optional string fun = 1;
+}
+
+message Function_ {
+ optional string fun1 = 1;
+}
diff --git a/test/protos/descriptor_2_5_opensource.proto b/test/protos/descriptor_2_5_opensource.proto
index a785f79..5832c61 100644
--- a/test/protos/descriptor_2_5_opensource.proto
+++ b/test/protos/descriptor_2_5_opensource.proto
@@ -1,3 +1,5 @@
+syntax = "proto2";
+
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
diff --git a/test/service_generator_test.dart b/test/service_generator_test.dart
index a1c773e..b917228 100644
--- a/test/service_generator_test.dart
+++ b/test/service_generator_test.dart
@@ -35,7 +35,7 @@
}
Map<String, dynamic> get $json => Test$json;
- Map<String, dynamic> get $messageJson => Test$messageJson;
+ Map<String, Map<String, dynamic>> get $messageJson => Test$messageJson;
}
''';
diff --git a/test/service_test.dart b/test/service_test.dart
index 05edda0..39a8943 100644
--- a/test/service_test.dart
+++ b/test/service_test.dart
@@ -55,6 +55,7 @@
class FakeJsonClient implements RpcClient {
final FakeJsonServer server;
+
FakeJsonClient(this.server);
Future<GeneratedMessage> invoke(