Sync from internal repo. (#91)

Avoid name clashes between fields and import prefix, and between enum
and extension class names.

Fixes #56.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 309c27a..88bf238 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## Not yet released
+
+* Avoid name clashes between import prefix and field names.
+* Avoid name clashes between generated enum and extension class names.
+
 ## 0.7.6 - 2017-08-22
 
 * Updated gRPC client stub generation to produce code matching latest changes to
diff --git a/Makefile b/Makefile
index da0efb8..8b92a17 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,10 @@
 	google/protobuf/unittest \
 	dart_name \
 	enum_extension \
+	ExtensionEnumNameConflict \
 	ExtensionNameConflict \
+	foo \
+	import_clash \
 	map_api \
 	map_api2 \
 	mixins \
diff --git a/lib/code_generator.dart b/lib/code_generator.dart
index ecb10e0..8c17f01 100644
--- a/lib/code_generator.dart
+++ b/lib/code_generator.dart
@@ -8,7 +8,15 @@
   String get package;
   String get classname;
   String get fqname;
-  String get packageImportPrefix => package.replaceAll('.', r'$');
+  String get packageImportPrefix =>
+      _cachedImportPrefix ??= _calculateImportPrefix();
+
+  String _cachedImportPrefix;
+
+  String _calculateImportPrefix() {
+    final importName = package.replaceAll('.', r'$');
+    return importName.isNotEmpty ? '\$$importName' : '';
+  }
 
   /// The generator of the .pb.dart file defining this entity.
   ///
diff --git a/lib/file_generator.dart b/lib/file_generator.dart
index a4d6d9c..4290cac 100644
--- a/lib/file_generator.dart
+++ b/lib/file_generator.dart
@@ -362,7 +362,7 @@
     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"
+      out.println("// ignore_for_file: UNDEFINED_SHOWN_NAME,UNUSED_SHOWN_NAME\n"
           "import 'dart:core' show int, dynamic, String, List, Map;");
       out.println("import 'package:protobuf/protobuf.dart';");
       out.println();
@@ -530,8 +530,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library $libraryName;
 ''');
   }
diff --git a/lib/names.dart b/lib/names.dart
index b25b03c..d472beb 100644
--- a/lib/names.dart
+++ b/lib/names.dart
@@ -55,6 +55,9 @@
   for (var messageType in descriptor.messageType) {
     taken.add(messageClassName(messageType));
   }
+  for (var enumType in descriptor.enumType) {
+    taken.add(enumType.name);
+  }
 
   String s = _fileNameWithoutExtension(descriptor).replaceAll('-', '_');
   String candidate = '${s[0].toUpperCase()}${s.substring(1)}';
diff --git a/lib/src/dart_options.pb.dart b/lib/src/dart_options.pb.dart
index 0f67d7a..22e13a7 100644
--- a/lib/src/dart_options.pb.dart
+++ b/lib/src/dart_options.pb.dart
@@ -1,8 +1,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library dart_options_dart_options;
 
 // ignore: UNUSED_SHOWN_NAME
diff --git a/lib/src/descriptor.pb.dart b/lib/src/descriptor.pb.dart
index 9e0279a..b5ad945 100644
--- a/lib/src/descriptor.pb.dart
+++ b/lib/src/descriptor.pb.dart
@@ -1,8 +1,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library google.protobuf_descriptor;
 
 // ignore: UNUSED_SHOWN_NAME
diff --git a/lib/src/descriptor.pbenum.dart b/lib/src/descriptor.pbenum.dart
index 674f0fa..1408ca8 100644
--- a/lib/src/descriptor.pbenum.dart
+++ b/lib/src/descriptor.pbenum.dart
@@ -1,11 +1,10 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library google.protobuf_descriptor_pbenum;
 
-// ignore: UNUSED_SHOWN_NAME
+// ignore_for_file: UNDEFINED_SHOWN_NAME,UNUSED_SHOWN_NAME
 import 'dart:core' show int, dynamic, String, List, Map;
 import 'package:protobuf/protobuf.dart';
 
diff --git a/lib/src/plugin.pb.dart b/lib/src/plugin.pb.dart
index af5a678..a6247fc 100644
--- a/lib/src/plugin.pb.dart
+++ b/lib/src/plugin.pb.dart
@@ -1,8 +1,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library google.protobuf.compiler_plugin;
 
 // ignore: UNUSED_SHOWN_NAME
@@ -10,7 +9,7 @@
 
 import 'package:protobuf/protobuf.dart';
 
-import 'descriptor.pb.dart' as google$protobuf;
+import 'descriptor.pb.dart' as $google$protobuf;
 
 class Version extends GeneratedMessage {
   static final BuilderInfo _i = new BuilderInfo('Version')
@@ -81,12 +80,12 @@
     ..a<String>(2, 'parameter', PbFieldType.OS)
     ..a<Version>(3, 'compilerVersion', PbFieldType.OM, Version.getDefault,
         Version.create)
-    ..pp<google$protobuf.FileDescriptorProto>(
+    ..pp<$google$protobuf.FileDescriptorProto>(
         15,
         'protoFile',
         PbFieldType.PM,
-        google$protobuf.FileDescriptorProto.$checkItem,
-        google$protobuf.FileDescriptorProto.create);
+        $google$protobuf.FileDescriptorProto.$checkItem,
+        $google$protobuf.FileDescriptorProto.create);
 
   CodeGeneratorRequest() : super();
   CodeGeneratorRequest.fromBuffer(List<int> i,
@@ -130,7 +129,8 @@
   bool hasCompilerVersion() => $_has(2, 3);
   void clearCompilerVersion() => clearField(3);
 
-  List<google$protobuf.FileDescriptorProto> get protoFile => $_get(3, 15, null);
+  List<$google$protobuf.FileDescriptorProto> get protoFile =>
+      $_get(3, 15, null);
 }
 
 class _ReadonlyCodeGeneratorRequest extends CodeGeneratorRequest
diff --git a/test/all_tests.dart b/test/all_tests.dart
index e44667e..e3df543 100755
--- a/test/all_tests.dart
+++ b/test/all_tests.dart
@@ -14,6 +14,7 @@
 import 'generated_message_test.dart' as generated_message;
 import 'hash_code_test.dart' as hash_code;
 import 'indenting_writer_test.dart' as indenting_writer;
+import 'import_test.dart' as import_prefix;
 import 'json_test.dart' as json;
 import 'map_test.dart' as map;
 import 'message_generator_test.dart' as message_generator;
@@ -38,6 +39,7 @@
   generated_message.main();
   hash_code.main();
   indenting_writer.main();
+  import_prefix.main();
   json.main();
   map.main();
   message_generator.main();
diff --git a/test/client_generator_test.dart b/test/client_generator_test.dart
index 7b6cc74..adf6adf 100644
--- a/test/client_generator_test.dart
+++ b/test/client_generator_test.dart
@@ -23,8 +23,8 @@
     var emptyResponse = new SomeReply();
     return _client.invoke(ctx, 'Test', 'AMethod', request, emptyResponse);
   }
-  Future<foo$bar.AnotherReply> anotherMethod(ClientContext ctx, foo$bar.EmptyMessage request) {
-    var emptyResponse = new foo$bar.AnotherReply();
+  Future<$foo$bar.AnotherReply> anotherMethod(ClientContext ctx, $foo$bar.EmptyMessage request) {
+    var emptyResponse = new $foo$bar.AnotherReply();
     return _client.invoke(ctx, 'Test', 'AnotherMethod', request, emptyResponse);
   }
 }
diff --git a/test/extension_test.dart b/test/extension_test.dart
index 549891f..ce7bec0 100644
--- a/test/extension_test.dart
+++ b/test/extension_test.dart
@@ -11,6 +11,8 @@
 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 '../out/protos/ExtensionEnumNameConflict.pb.dart';
 
 import 'test_util.dart';
 
@@ -160,4 +162,12 @@
     var msg = new Extendable();
     msg.setExtension(Enum_extension.animal, Animal.CAT);
   });
+
+  test('extension class was renamed to avoid conflict with message', () {
+    expect(ExtensionNameConflictExt.someExtension.tagNumber, 1);
+  });
+
+  test('extension class was renamed to avoid conflict with enum', () {
+    expect(ExtensionEnumNameConflictExt.enumConflictExtension.tagNumber, 1);
+  });
 }
diff --git a/test/file_generator_test.dart b/test/file_generator_test.dart
index d759663..eb09834 100644
--- a/test/file_generator_test.dart
+++ b/test/file_generator_test.dart
@@ -76,8 +76,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test;
 
 // ignore: UNUSED_SHOWN_NAME
@@ -141,8 +140,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test_pbjson;
 
 const PhoneNumber$json = const {
@@ -170,8 +168,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test;
 
 // ignore: UNUSED_SHOWN_NAME
@@ -185,11 +182,10 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test_pbenum;
 
-// ignore: UNUSED_SHOWN_NAME
+// ignore_for_file: UNDEFINED_SHOWN_NAME,UNUSED_SHOWN_NAME
 import 'dart:core' show int, dynamic, String, List, Map;
 import 'package:protobuf/protobuf.dart';
 
@@ -233,8 +229,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test_pbjson;
 
 const PhoneType$json = const {
@@ -264,8 +259,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library pb_library_test;
 
 // ignore: UNUSED_SHOWN_NAME
@@ -292,8 +286,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test;
 
 // ignore: UNUSED_SHOWN_NAME
@@ -330,8 +323,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test;
 
 import 'dart:async';
@@ -380,8 +372,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test_pbserver;
 
 import 'dart:async';
@@ -448,8 +439,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test;
 
 // ignore: UNUSED_SHOWN_NAME
@@ -512,8 +502,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test_pbgrpc;
 
 import 'dart:async';
@@ -678,8 +667,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test;
 
 // ignore: UNUSED_SHOWN_NAME
@@ -687,14 +675,14 @@
 
 import 'package:protobuf/protobuf.dart';
 
-import 'package1.pb.dart' as p1;
-import 'package2.pb.dart' as p2;
+import 'package1.pb.dart' as $p1;
+import 'package2.pb.dart' as $p2;
 
 class M extends GeneratedMessage {
   static final BuilderInfo _i = new BuilderInfo('M')
     ..a<M>(1, 'm', PbFieldType.OM, M.getDefault, M.create)
-    ..a<p1.M>(2, 'm1', PbFieldType.OM, p1.M.getDefault, p1.M.create)
-    ..a<p2.M>(3, 'm2', PbFieldType.OM, p2.M.getDefault, p2.M.create)
+    ..a<$p1.M>(2, 'm1', PbFieldType.OM, $p1.M.getDefault, $p1.M.create)
+    ..a<$p2.M>(3, 'm2', PbFieldType.OM, $p2.M.getDefault, $p2.M.create)
     ..hasRequiredFields = false
   ;
 
@@ -719,13 +707,13 @@
   bool hasM() => $_has(0, 1);
   void clearM() => clearField(1);
 
-  p1.M get m1 => $_get(1, 2, null);
-  set m1(p1.M v) { setField(2, v); }
+  $p1.M get m1 => $_get(1, 2, null);
+  set m1($p1.M v) { setField(2, v); }
   bool hasM1() => $_has(1, 2);
   void clearM1() => clearField(2);
 
-  p2.M get m2 => $_get(2, 3, null);
-  set m2(p2.M v) { setField(3, v); }
+  $p2.M get m2 => $_get(2, 3, null);
+  set m2($p2.M v) { setField(3, v); }
   bool hasM2() => $_has(2, 3);
   void clearM2() => clearField(3);
 }
@@ -738,8 +726,7 @@
 ///
 //  Generated code. Do not modify.
 ///
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes
 library test_pbjson;
 
 const M$json = const {
diff --git a/test/import_test.dart b/test/import_test.dart
new file mode 100644
index 0000000..02b57c8
--- /dev/null
+++ b/test/import_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library import_test;
+
+import 'package:test/test.dart';
+
+import '../out/protos/import_clash.pb.dart' as pb;
+import '../out/protos/foo.pb.dart' as foo;
+
+void main() {
+  test('Import prefixes in generated files do not clash with fields', () {
+    new pb.Clasher()..foo = new foo.Foo();
+  });
+}
diff --git a/test/protos/ExtensionEnumNameConflict.proto b/test/protos/ExtensionEnumNameConflict.proto
new file mode 100644
index 0000000..e26f8d0
--- /dev/null
+++ b/test/protos/ExtensionEnumNameConflict.proto
@@ -0,0 +1,16 @@
+syntax = "proto2";
+
+package protobuf_unittest;
+
+// Cause a conflict with the auto-generated extension class.
+enum ExtensionEnumNameConflict {
+  UNKNOWN = 0;
+}
+
+message YetAnotherMessageToBeExtended {
+  extensions 1 to max;
+}
+
+extend YetAnotherMessageToBeExtended {
+  optional int32 enumConflictExtension = 1;
+}
diff --git a/test/protos/foo.proto b/test/protos/foo.proto
new file mode 100644
index 0000000..2a4c36b
--- /dev/null
+++ b/test/protos/foo.proto
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+syntax = "proto2";
+
+package foo;
+
+message Foo {
+}
diff --git a/test/protos/import_clash.proto b/test/protos/import_clash.proto
new file mode 100644
index 0000000..ae9ecd9
--- /dev/null
+++ b/test/protos/import_clash.proto
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+syntax = "proto2";
+
+import "foo.proto";
+
+package import_clash;
+
+message Clasher {
+  required foo.Foo foo = 1;
+}
diff --git a/test/service_generator_test.dart b/test/service_generator_test.dart
index fe5c82f..808675c 100644
--- a/test/service_generator_test.dart
+++ b/test/service_generator_test.dart
@@ -16,12 +16,12 @@
     String expected = r'''
 abstract class TestServiceBase extends GeneratedService {
   Future<SomeReply> aMethod(ServerContext ctx, SomeRequest request);
-  Future<foo$bar.AnotherReply> anotherMethod(ServerContext ctx, foo$bar.EmptyMessage request);
+  Future<$foo$bar.AnotherReply> anotherMethod(ServerContext ctx, $foo$bar.EmptyMessage request);
 
   GeneratedMessage createRequest(String method) {
     switch (method) {
       case 'AMethod': return new SomeRequest();
-      case 'AnotherMethod': return new foo$bar.EmptyMessage();
+      case 'AnotherMethod': return new $foo$bar.EmptyMessage();
       default: throw new ArgumentError('Unknown method: $method');
     }
   }