Add dart options for override annotations.

These make it possible to tag generated getters, setters, clearX()
and hasX() methods with "@override".

R=skybrian@google.com

Review URL: https://chromiumcodereview.appspot.com//2150323002 .
diff --git a/lib/message_generator.dart b/lib/message_generator.dart
index 1177151..7e1d2c1 100644
--- a/lib/message_generator.dart
+++ b/lib/message_generator.dart
@@ -311,11 +311,26 @@
       var defaultExpr = field.getDefaultExpr();
       var names = field.memberNames;
 
+      _emitOverrideIf(field.overridesGetter, out);
       out.println('${fieldTypeString} get ${names.fieldName}'
           ' => \$_get('
           '${field.index}, ${field.number}, $defaultExpr);');
-      if (!field.isRepeated) {
+      if (field.isRepeated) {
+        if (field.overridesSetter) {
+          throw 'Field ${field.fqname} cannot override a setter for '
+              '${names.fieldName} because it is repeated.';
+        }
+        if (field.overridesHasMethod) {
+          throw 'Field ${field.fqname} cannot override '
+              '${names.hasMethodName}() because it is repeated.';
+        }
+        if (field.overridesClearMethod) {
+          throw 'Field ${field.fqname} cannot override '
+              '${names.clearMethodName}() because it is repeated.';
+        }
+      } else {
         var fastSetter = field.baseType.setter;
+        _emitOverrideIf(field.overridesSetter, out);
         if (fastSetter != null) {
           out.println('void set ${names.fieldName}'
               '($fieldTypeString v) { '
@@ -327,14 +342,22 @@
               'setField(${field.number}, v);'
               ' }');
         }
+        _emitOverrideIf(field.overridesHasMethod, out);
         out.println('bool ${names.hasMethodName}() =>'
             ' \$_has(${field.index}, ${field.number});');
+        _emitOverrideIf(field.overridesClearMethod, out);
         out.println('void ${names.clearMethodName}() =>'
             ' clearField(${field.number});');
       }
     }
   }
 
+  void _emitOverrideIf(bool condition, IndentingWriter out) {
+    if (condition) {
+      out.println('@override');
+    }
+  }
+
   void generateEnums(IndentingWriter out) {
     for (EnumGenerator e in _enumGenerators) {
       e.generate(out);
diff --git a/lib/protobuf_field.dart b/lib/protobuf_field.dart
index 7819dd2..f7e3159 100644
--- a/lib/protobuf_field.dart
+++ b/lib/protobuf_field.dart
@@ -55,6 +55,20 @@
   bool get isPacked =>
       isRepeated && descriptor.options != null && descriptor.options.packed;
 
+  /// Whether the field has the `overrideGetter` annotation set to true.
+  bool get overridesGetter => _hasBooleanOption(Dart_options.overrideGetter);
+
+  /// Whether the field has the `overrideSetter` annotation set to true.
+  bool get overridesSetter => _hasBooleanOption(Dart_options.overrideSetter);
+
+  /// Whether the field has the `overrideHasMethod` annotation set to true.
+  bool get overridesHasMethod =>
+      _hasBooleanOption(Dart_options.overrideHasMethod);
+
+  /// Whether the field has the `overrideClearMethod` annotation set to true.
+  bool get overridesClearMethod =>
+      _hasBooleanOption(Dart_options.overrideClearMethod);
+
   /// True if this field uses the Int64 from the fixnum package.
   bool get needsFixnumImport => baseType.unprefixed == "Int64";
 
@@ -243,6 +257,9 @@
     return noDefault;
   }
 
+  bool _hasBooleanOption(Extension extension) =>
+      descriptor?.options?.getExtension(extension) ?? false;
+
   get _invalidDefaultValue => "dart-protoc-plugin:"
       " invalid default value (${descriptor.defaultValue})"
       " found in field $fqname";
diff --git a/lib/src/dart_options.pb.dart b/lib/src/dart_options.pb.dart
index b843737..ae503d6 100644
--- a/lib/src/dart_options.pb.dart
+++ b/lib/src/dart_options.pb.dart
@@ -78,11 +78,19 @@
   static final Extension imports = new Extension<Imports>('FileOptions', 'imports', 28125061, PbFieldType.OM, Imports.getDefault, Imports.create);
   static final Extension defaultMixin = new Extension<String>('FileOptions', 'defaultMixin', 96128839, PbFieldType.OS);
   static final Extension mixin = new Extension<String>('MessageOptions', 'mixin', 96128839, PbFieldType.OS);
+  static final Extension overrideGetter = new Extension<bool>('FieldOptions', 'overrideGetter', 28205290, PbFieldType.OB);
+  static final Extension overrideSetter = new Extension<bool>('FieldOptions', 'overrideSetter', 28937366, PbFieldType.OB);
+  static final Extension overrideHasMethod = new Extension<bool>('FieldOptions', 'overrideHasMethod', 28937461, PbFieldType.OB);
+  static final Extension overrideClearMethod = new Extension<bool>('FieldOptions', 'overrideClearMethod', 28907907, PbFieldType.OB);
   static final Extension dartName = new Extension<String>('FieldOptions', 'dartName', 28700919, PbFieldType.OS);
   static void registerAllExtensions(ExtensionRegistry registry) {
     registry.add(imports);
     registry.add(defaultMixin);
     registry.add(mixin);
+    registry.add(overrideGetter);
+    registry.add(overrideSetter);
+    registry.add(overrideHasMethod);
+    registry.add(overrideClearMethod);
     registry.add(dartName);
   }
 }
diff --git a/test/protos/dart_options.proto b/test/protos/dart_options.proto
index 8056518..265dd39 100644
--- a/test/protos/dart_options.proto
+++ b/test/protos/dart_options.proto
@@ -59,6 +59,19 @@
 }
 
 extend google.protobuf.FieldOptions {
+  // Adds @override annotation to the field's getter (for use with mixins).
+  optional bool override_getter = 28205290;
+
+  // Adds @override annotation to the field's setter (for use with mixins).
+  optional bool override_setter = 28937366;
+
+  // Adds @override annotation to the field's hasX() method (for use with
+  // mixins).
+  optional bool override_has_method = 28937461;
+
+  // Adds @override annotation to the field's clearX() method (for use with
+  // mixins).
+  optional bool override_clear_method = 28907907;
 
   // Uses the given name for getters, setters and as suffixes for has/clear
   // methods in the generated Dart file. Should be lowerCamelCase.
diff --git a/test/protos/mixins.proto b/test/protos/mixins.proto
index 48352a8..9395e52 100644
--- a/test/protos/mixins.proto
+++ b/test/protos/mixins.proto
@@ -8,8 +8,8 @@
     import_from: "package:protoc_plugin/testing/mixins.dart"
   },
   {
-   name: "Mixin2"
-   import_from: "package:protoc_plugin/testing/mixins.dart"
+    name: "Mixin2"
+    import_from: "package:protoc_plugin/testing/mixins.dart"
   },
   {
     name: "Mixin3"
@@ -25,17 +25,27 @@
 }
 
 message Mixin1PB {
-  optional string interface_string = 1;
+  optional string interface_string = 1 [
+    (dart_options.override_getter) = true,
+    (dart_options.override_setter) = true,
+    (dart_options.override_has_method) = true
+  ];
 }
 
 message Mixin2PB {
   option (dart_options.mixin) = "Mixin2";
 
-  optional string overridden_has_method = 3;
+  optional string overridden_has_method = 3 [
+    (dart_options.override_has_method) = true
+  ];
 }
 
 message Mixin3PB {
   option (dart_options.mixin) = "Mixin3";
 
-  optional string interface_string = 1;
+  optional string interface_string = 1 [
+    (dart_options.override_getter) = true,
+    (dart_options.override_setter) = true,
+    (dart_options.override_has_method) = true
+  ];
 }