Avoid name collisions with Int64 and enum names beginning with digits. (#299)

* Avoid name collisions with Int64 and enum names beginning with digits.
diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md
index 67fa524..a7d6a3f 100644
--- a/protoc_plugin/CHANGELOG.md
+++ b/protoc_plugin/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 19.0.1
+
+* Fix: avoid naming collisions with `Int64` and enum names beginning with digits
+  after an initial underscore.
+
 ## 19.0.0+1
 * Updated protobuf dependency to '>=0.14.4 <2.0.0' to allow 1.0.0.
 
diff --git a/protoc_plugin/lib/base_type.dart b/protoc_plugin/lib/base_type.dart
index 8c98f67..d447ce3 100644
--- a/protoc_plugin/lib/base_type.dart
+++ b/protoc_plugin/lib/base_type.dart
@@ -85,19 +85,19 @@
             "SF3", "$_coreImportPrefix.int", r"$_setSignedInt32", null);
       case FieldDescriptorProto_Type.TYPE_INT64:
         return const BaseType._raw(FieldDescriptorProto_Type.TYPE_INT64, "6",
-            "Int64", r"$_setInt64", null);
+            "$_fixnumImportPrefix.Int64", r"$_setInt64", null);
       case FieldDescriptorProto_Type.TYPE_UINT64:
         return const BaseType._raw(FieldDescriptorProto_Type.TYPE_UINT64, "U6",
-            "Int64", r"$_setInt64", null);
+            "$_fixnumImportPrefix.Int64", r"$_setInt64", null);
       case FieldDescriptorProto_Type.TYPE_SINT64:
         return const BaseType._raw(FieldDescriptorProto_Type.TYPE_SINT64, "S6",
-            "Int64", r"$_setInt64", null);
+            "$_fixnumImportPrefix.Int64", r"$_setInt64", null);
       case FieldDescriptorProto_Type.TYPE_FIXED64:
         return const BaseType._raw(FieldDescriptorProto_Type.TYPE_FIXED64, "F6",
-            "Int64", r"$_setInt64", null);
+            "$_fixnumImportPrefix.Int64", r"$_setInt64", null);
       case FieldDescriptorProto_Type.TYPE_SFIXED64:
         return const BaseType._raw(FieldDescriptorProto_Type.TYPE_SFIXED64,
-            "SF6", "Int64", r"$_setInt64", null);
+            "SF6", "$_fixnumImportPrefix.Int64", r"$_setInt64", null);
       case FieldDescriptorProto_Type.TYPE_STRING:
         return const BaseType._raw(FieldDescriptorProto_Type.TYPE_STRING, "S",
             "$_coreImportPrefix.String", r"$_setString", null);
diff --git a/protoc_plugin/lib/file_generator.dart b/protoc_plugin/lib/file_generator.dart
index 91c357c..7e27ba5 100644
--- a/protoc_plugin/lib/file_generator.dart
+++ b/protoc_plugin/lib/file_generator.dart
@@ -9,6 +9,7 @@
 const String _protobufImportPrefix = r'$pb';
 const String _asyncImportPrefix = r'$async';
 const String _coreImportPrefix = r'$core';
+const String _fixnumImportPrefix = r'$fixnum';
 const String _grpcImportPrefix = r'$grpc';
 const String _mixinImportPrefix = r'$mixin';
 const String _protobufImport =
@@ -290,7 +291,8 @@
     out.println();
 
     if (_needsFixnumImport) {
-      out.println("import 'package:fixnum/fixnum.dart';");
+      out.println(
+          "import 'package:fixnum/fixnum.dart' as $_fixnumImportPrefix;");
     }
 
     if (_needsProtobufImport) {
diff --git a/protoc_plugin/lib/message_generator.dart b/protoc_plugin/lib/message_generator.dart
index 221d827..c4a6428 100644
--- a/protoc_plugin/lib/message_generator.dart
+++ b/protoc_plugin/lib/message_generator.dart
@@ -570,7 +570,7 @@
       }
       return '\$_getI($index, $defaultExpr)';
     }
-    if (fieldType == 'Int64' && defaultExpr == 'null') {
+    if (fieldType == '$_fixnumImportPrefix.Int64' && defaultExpr == 'null') {
       return '\$_getI64($index)';
     }
     if (defaultExpr == 'null') {
diff --git a/protoc_plugin/lib/names.dart b/protoc_plugin/lib/names.dart
index 56bbf74..c712741 100644
--- a/protoc_plugin/lib/names.dart
+++ b/protoc_plugin/lib/names.dart
@@ -70,16 +70,24 @@
       this.whichOneofMethodName, this.oneofEnumName, this.byTagMapName);
 }
 
+// For performance reasons, use code units instead of Regex.
+bool _startsWithDigit(String input) =>
+    input.isNotEmpty && (input.codeUnitAt(0) ^ 0x30) <= 9;
+
 /// Move any initial underscores in [input] to the end.
 ///
 /// According to the spec identifiers cannot start with _, but it seems to be
-/// accepted by protoc.
+/// accepted by protoc. These identifiers are private in Dart, so they have to
+/// be transformed.
 ///
-/// These identifiers are private in Dart, so they have to be transformed.
+/// If [input] starts with a digit after transformation, prefix with an 'x'.
 String avoidInitialUnderscore(String input) {
   while (input.startsWith('_')) {
     input = '${input.substring(1)}_';
   }
+  if (_startsWithDigit(input)) {
+    input = 'x$input';
+  }
   return input;
 }
 
diff --git a/protoc_plugin/lib/protobuf_field.dart b/protoc_plugin/lib/protobuf_field.dart
index 2d9c79e..2d41875 100644
--- a/protoc_plugin/lib/protobuf_field.dart
+++ b/protoc_plugin/lib/protobuf_field.dart
@@ -81,7 +81,8 @@
       _hasBooleanOption(Dart_options.overrideClearMethod);
 
   /// True if this field uses the Int64 from the fixnum package.
-  bool get needsFixnumImport => baseType.unprefixed == "Int64";
+  bool get needsFixnumImport =>
+      baseType.unprefixed == "$_fixnumImportPrefix.Int64";
 
   /// True if this field is a map field definition:
   /// `map<key_type, value_type> map_field = N`.
@@ -237,8 +238,8 @@
             break;
         }
       } else {
-        if (makeDefault == 'Int64.ZERO' &&
-            type == 'Int64' &&
+        if (makeDefault == '$_fixnumImportPrefix.Int64.ZERO' &&
+            type == '$_fixnumImportPrefix.Int64' &&
             typeConstant == '$_protobufImportPrefix.PbFieldType.O6') {
           invocation = 'aInt64';
         } else {
@@ -325,7 +326,7 @@
       case FieldDescriptorProto_Type.TYPE_SFIXED64:
         var value = '0';
         if (descriptor.hasDefaultValue()) value = descriptor.defaultValue;
-        if (value == '0') return 'Int64.ZERO';
+        if (value == '0') return '$_fixnumImportPrefix.Int64.ZERO';
         return "$_protobufImportPrefix.parseLongInt('$value')";
       case FieldDescriptorProto_Type.TYPE_STRING:
         return _getDefaultAsStringExpr(null);
diff --git a/protoc_plugin/lib/src/descriptor.pb.dart b/protoc_plugin/lib/src/descriptor.pb.dart
index 59408ef..cddd9ed 100644
--- a/protoc_plugin/lib/src/descriptor.pb.dart
+++ b/protoc_plugin/lib/src/descriptor.pb.dart
@@ -7,7 +7,7 @@
 
 import 'dart:core' as $core;
 
-import 'package:fixnum/fixnum.dart';
+import 'package:fixnum/fixnum.dart' as $fixnum;
 import 'package:protobuf/protobuf.dart' as $pb;
 
 import 'descriptor.pbenum.dart';
@@ -1704,8 +1704,8 @@
     ..pc<UninterpretedOption_NamePart>(2, 'name', $pb.PbFieldType.PM,
         subBuilder: UninterpretedOption_NamePart.create)
     ..aOS(3, 'identifierValue')
-    ..a<Int64>(4, 'positiveIntValue', $pb.PbFieldType.OU6,
-        defaultOrMaker: Int64.ZERO)
+    ..a<$fixnum.Int64>(4, 'positiveIntValue', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
     ..aInt64(5, 'negativeIntValue')
     ..a<$core.double>(6, 'doubleValue', $pb.PbFieldType.OD)
     ..a<$core.List<$core.int>>(7, 'stringValue', $pb.PbFieldType.OY)
@@ -1749,9 +1749,9 @@
   void clearIdentifierValue() => clearField(3);
 
   @$pb.TagNumber(4)
-  Int64 get positiveIntValue => $_getI64(2);
+  $fixnum.Int64 get positiveIntValue => $_getI64(2);
   @$pb.TagNumber(4)
-  set positiveIntValue(Int64 v) {
+  set positiveIntValue($fixnum.Int64 v) {
     $_setInt64(2, v);
   }
 
@@ -1761,9 +1761,9 @@
   void clearPositiveIntValue() => clearField(4);
 
   @$pb.TagNumber(5)
-  Int64 get negativeIntValue => $_getI64(3);
+  $fixnum.Int64 get negativeIntValue => $_getI64(3);
   @$pb.TagNumber(5)
-  set negativeIntValue(Int64 v) {
+  set negativeIntValue($fixnum.Int64 v) {
     $_setInt64(3, v);
   }
 
diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml
index 0e706af..a96bf41 100644
--- a/protoc_plugin/pubspec.yaml
+++ b/protoc_plugin/pubspec.yaml
@@ -1,5 +1,5 @@
 name: protoc_plugin
-version: 19.0.0+1
+version: 19.0.1
 author: Dart Team <misc@dartlang.org>
 description: Protoc compiler plugin to generate Dart code
 homepage: https://github.com/dart-lang/protobuf
diff --git a/protoc_plugin/test/file_generator_test.dart b/protoc_plugin/test/file_generator_test.dart
index 12f0a94..84cf9c9 100644
--- a/protoc_plugin/test/file_generator_test.dart
+++ b/protoc_plugin/test/file_generator_test.dart
@@ -69,6 +69,22 @@
           ..defaultValue = r'$'
       ]));
   }
+  return fd;
+}
+
+FileDescriptorProto createInt64Proto() {
+  FileDescriptorProto fd = FileDescriptorProto()..name = 'test';
+  fd.messageType.add(DescriptorProto()
+    ..name = 'Int64'
+    ..field.add(
+      // optional int64 value = 1;
+      FieldDescriptorProto()
+        ..name = 'value'
+        ..jsonName = 'value'
+        ..number = 1
+        ..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
+        ..type = FieldDescriptorProto_Type.TYPE_INT64,
+    ));
 
   return fd;
 }
@@ -85,6 +101,16 @@
         fg.generateMainFile().toString(), 'test/goldens/oneMessage.pb');
   });
 
+  test('FileGenerator outputs a .pb.dart file for an Int64 message', () {
+    FileDescriptorProto fd = createInt64Proto();
+    var options =
+        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    FileGenerator fg = FileGenerator(fd, options);
+    link(options, [fg]);
+    expectMatchesGoldenFile(
+        fg.generateMainFile().toString(), 'test/goldens/int64.pb');
+  });
+
   test(
       'FileGenerator outputs a .pb.dart.meta file for a proto with one message',
       () {
diff --git a/protoc_plugin/test/goldens/header_with_fixnum.pb b/protoc_plugin/test/goldens/header_with_fixnum.pb
index eb98d65..b5bc420 100644
--- a/protoc_plugin/test/goldens/header_with_fixnum.pb
+++ b/protoc_plugin/test/goldens/header_with_fixnum.pb
@@ -7,6 +7,6 @@
 
 import 'dart:core' as $core;
 
-import 'package:fixnum/fixnum.dart';
+import 'package:fixnum/fixnum.dart' as $fixnum;
 import 'package:protobuf/protobuf.dart' as $pb;
 
diff --git a/protoc_plugin/test/goldens/int64.pb b/protoc_plugin/test/goldens/int64.pb
new file mode 100644
index 0000000..df1f41d
--- /dev/null
+++ b/protoc_plugin/test/goldens/int64.pb
@@ -0,0 +1,43 @@
+///
+//  Generated code. Do not modify.
+//  source: test
+//
+// @dart = 2.3
+// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type
+
+import 'dart:core' as $core;
+
+import 'package:fixnum/fixnum.dart' as $fixnum;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class Int64 extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo('Int64', createEmptyInstance: create)
+    ..aInt64(1, 'value')
+    ..hasRequiredFields = false
+  ;
+
+  Int64._() : super();
+  factory Int64() => create();
+  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);
+  Int64 clone() => Int64()..mergeFromMessage(this);
+  Int64 copyWith(void Function(Int64) updates) => super.copyWith((message) => updates(message as Int64));
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static Int64 create() => Int64._();
+  Int64 createEmptyInstance() => create();
+  static $pb.PbList<Int64> createRepeated() => $pb.PbList<Int64>();
+  @$core.pragma('dart2js:noInline')
+  static Int64 getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Int64>(create);
+  static Int64 _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get value => $_getI64(0);
+  @$pb.TagNumber(1)
+  set value($fixnum.Int64 v) { $_setInt64(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasValue() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearValue() => clearField(1);
+}
+
diff --git a/protoc_plugin/test/leading_underscores_test.dart b/protoc_plugin/test/leading_underscores_test.dart
index 61c4aee..933d198 100644
--- a/protoc_plugin/test/leading_underscores_test.dart
+++ b/protoc_plugin/test/leading_underscores_test.dart
@@ -50,6 +50,9 @@
     expect(messageA.e, Enum_.constant);
     messageA.clearE();
     expect(messageA.e, Enum_.default_);
+    messageA.clearE();
+    messageA.e = Enum_.x1digit_;
+    expect(messageA.e, Enum_.x1digit_);
     messageA.r.add(message);
     expect(messageA.r, [message]);
     messageA.setExtension(Leading_underscores_.q, Int64(100));
diff --git a/protoc_plugin/test/names_test.dart b/protoc_plugin/test/names_test.dart
index 9a5372c..f8d2410 100644
--- a/protoc_plugin/test/names_test.dart
+++ b/protoc_plugin/test/names_test.dart
@@ -129,6 +129,8 @@
     expect(names.avoidInitialUnderscore('foo_'), 'foo_');
     expect(names.avoidInitialUnderscore('_foo'), 'foo_');
     expect(names.avoidInitialUnderscore('__foo'), 'foo__');
+    expect(names.avoidInitialUnderscore('_6E'), 'x6E_');
+    expect(names.avoidInitialUnderscore('__6E'), 'x6E__');
   });
 
   test('legalDartIdentifier', () {
diff --git a/protoc_plugin/test/protos/_leading_underscores.proto b/protoc_plugin/test/protos/_leading_underscores.proto
index 49067ba..df91552 100644
--- a/protoc_plugin/test/protos/_leading_underscores.proto
+++ b/protoc_plugin/test/protos/_leading_underscores.proto
@@ -48,6 +48,7 @@
 enum _Enum {
   _default = 0;
   constant = 1;
+  _1digit = 2;
 }
 
 service _service {