Repository: google/protobuf.dart

Issue:
Three "hash" related members are not showing up in Dart 2.14.x
```
  'hash',
  'hashAll',
  'hashAllUnordered',
```

Requires a one-off hack in reserved names test


Discussion: My understanding of this issue:

In the library we maintain a list of "reserved names": https://github.com/google/protobuf.dart/blob/ded1ac7d2e00b1f8ff7e9ac4129a3be6c652ee94/protobuf/lib/meta.dart#L12-L86

When a proto file has a field name that when converted to camelCase conflicts with the names in the list above we add a suffix to the field name. E.g. `int32 hash_code = 5` in a proto file becomes `hashCode_5` in the generated Dart.

We want this list to include all `GeneratedMessage` methods (inherited or otherwise) so that user-written field names won't be overriding or be in conflict with a `GeneratedMessage` method. We test this in tests like:

https://github.com/google/protobuf.dart/blob/ded1ac7d2e00b1f8ff7e9ac4129a3be6c652ee94/protoc_plugin/test/reserved_names_test.dart#L26-L34

Now, Dart 2.14 adds 3 new static methods to `Object`:

- [hash](https://api.dart.dev/stable/2.14.0/dart-core/Object/hash.html)
- [hashAll](https://api.dart.dev/stable/2.14.0/dart-core/Object/hashAll.html)
- [hashAllUnordered](https://api.dart.dev/stable/2.14.0/dart-core/Object/hashAllUnordered.html)

and the function we use to get members of a class ([findMemberNames](https://github.com/google/protobuf.dart/blob/ded1ac7d2e00b1f8ff7e9ac4129a3be6c652ee94/protoc_plugin/test/mirror_util.dart#L7-L41)) also returns those static methods.

So in pseudo code `findMemberNames('GeneratedCode)` now returns extra names `hash`, `hashAll`, and `hashAllUnordered` which we don't have in the expected set of reserved names, which causes test failures when run with Dart 2.14 or newer.

---

**Should we make `hash`, `hashAll`, and `hashAllUnordered` reserved?** I think no, because they're static methods and so cannot be called via a `GeneratedMessage` instance and it's not possible to override them in `GeneratedMessage` subclasses. So user field names `hash`, `hash_all`, or `hash_all_unordered` will not override `Object.hashAll` for the generated Dart class.

Since static methods cannot be overridden and cannot be called on instances directly (using method call syntax), in general we don't want to count static methods in `findMemberNames`, but I think this is not possible to do today because [`DeclarationMirror`](https://api.dart.dev/stable/2.16.1/dart-mirrors/DeclarationMirror-class.html) does not have a property for checking whether the declaration is `static`, and it's unclear whether it will be implemented as apparently the sdk team is planning on discontinuing the support for it: https://github.com/dart-lang/sdk/issues/44489

So it seems to me like 51f4be0 is the right approach at least for now (until there's a `isStatic` property in `DeclarationMirror`), we should just remove TODOs and rename `_oneOffNames` and document it.

Any thoughts @kevmoo @sigurdm?

The issue description says "hash related member are not showing up ..." but actually the problem is that they *are* showing up. I hope I didn't completely misunderstand the issue here.
Static members of Object shouldn't cause any problems. Chat me directly if you want to...chat...
> Static members of Object shouldn't cause any problems

I agree! That's why I say we shouldn't add static members to `GeneratedMessage_reservedNames` and instead remove them from `findMemberNames` output.
Oh. Yeah. Sure!

On Wed, Apr 13, 2022, 02:35 Ömer Sinan Ağacan ***@***.***>
wrote:

> Static members of Object shouldn't cause any problems
>
> I agree! That's why I say we shouldn't add static members to
> GeneratedMessage_reservedNames and instead remove them from
> findMemberNames output.
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/google/protobuf.dart/issues/527#issuecomment-1097808813>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAAEFCQUBORWJMFWICBS74TVE2IOPANCNFSM5A4VJ3XQ>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>


Fix patch:
diff --git a/protobuf/lib/meta.dart b/protobuf/lib/meta.dart
index 6601c023c..73a65e8e6 100644
--- a/protobuf/lib/meta.dart
+++ b/protobuf/lib/meta.dart
@@ -60,7 +60,6 @@ const GeneratedMessage_reservedNames = <String>[
   'writeToCodedBufferWriter',
   'writeToJson',
   'writeToJsonMap',
-  r'$_defaultFor',
   r'$_ensure',
   r'$_get',
   r'$_getI64',
@@ -91,7 +90,6 @@ const ProtobufEnum_reservedNames = <String>[
   'Object',
   'ProtobufEnum',
   'hashCode',
-  'initByValue',
   'noSuchMethod',
   'runtimeType',
   'toString'


Test patch:
diff --git a/protoc_plugin/test/mirror_util.dart b/protoc_plugin/test/mirror_util.dart
index 959a4f5dd..70a74879f 100644
--- a/protoc_plugin/test/mirror_util.dart
+++ b/protoc_plugin/test/mirror_util.dart
@@ -4,8 +4,8 @@
 
 import 'dart:mirrors';
 
-/// Returns the names of the public properties and methods on a class.
-/// (Also visits its superclasses, recursively.)
+/// Returns the names of the public properties and non-static methods of a
+/// class. Also visits its superclasses, recursively.
 Set<String> findMemberNames(String importName, Symbol classSymbol) {
   var lib = currentMirrorSystem().libraries[Uri.parse(importName)]!;
   var cls = lib.declarations[classSymbol] as ClassMirror?;
@@ -26,7 +26,8 @@ Set<String> findMemberNames(String importName, Symbol classSymbol) {
     for (var decl in cls.declarations.values) {
       if (!decl.isPrivate &&
           decl is! VariableMirror &&
-          decl is! TypeVariableMirror) {
+          decl is! TypeVariableMirror &&
+          !(decl is MethodMirror && decl.isStatic)) {
         result.add(chooseName(decl.simpleName));
       }
     }
diff --git a/protoc_plugin/test/reserved_names_test.dart b/protoc_plugin/test/reserved_names_test.dart
index dbac073af..dfc19adc8 100755
--- a/protoc_plugin/test/reserved_names_test.dart
+++ b/protoc_plugin/test/reserved_names_test.dart
@@ -26,9 +26,7 @@ void main() {
   test('GeneratedMessage reserved names are up to date', () {
     var actual = Set<String>.from(GeneratedMessage_reservedNames);
     var expected =
-        findMemberNames('package:protobuf/protobuf.dart', #GeneratedMessage)
-          // TODO: see https://github.com/google/protobuf.dart/issues/527
-          ..removeAll(_oneOffNames);
+        findMemberNames('package:protobuf/protobuf.dart', #GeneratedMessage);
 
     expect(actual.toList()..sort(), equals(expected.toList()..sort()));
   });
@@ -36,18 +34,14 @@ void main() {
   test('ProtobufEnum reserved names are up to date', () {
     var actual = Set<String>.from(ProtobufEnum_reservedNames);
     var expected =
-        findMemberNames('package:protobuf/protobuf.dart', #ProtobufEnum)
-          // TODO: see https://github.com/google/protobuf.dart/issues/527
-          ..removeAll(_oneOffNames);
+        findMemberNames('package:protobuf/protobuf.dart', #ProtobufEnum);
 
     expect(actual.toList()..sort(), equals(expected.toList()..sort()));
   });
 
   test("ReadonlyMessageMixin doesn't add any reserved names", () {
-    var mixinNames =
-        findMemberNames('package:protobuf/protobuf.dart', #ReadonlyMessageMixin)
-          // TODO: see https://github.com/google/protobuf.dart/issues/527
-          ..removeAll(_oneOffNames);
+    var mixinNames = findMemberNames(
+        'package:protobuf/protobuf.dart', #ReadonlyMessageMixin);
     var reservedNames = Set<String>.from(GeneratedMessage_reservedNames);
     for (var name in mixinNames) {
       if (name == 'ReadonlyMessageMixin' || name == 'unknownFields') continue;
@@ -63,9 +57,7 @@ void main() {
 
     var expected = findMemberNames(meta.importFrom, #PbMapMixin)
       ..addAll(findMemberNames('dart:collection', #MapMixin))
-      ..removeAll(GeneratedMessage_reservedNames)
-      // TODO: see https://github.com/google/protobuf.dart/issues/527
-      ..removeAll(_oneOffNames);
+      ..removeAll(GeneratedMessage_reservedNames);
 
     expect(
         actual.toList()..sort(), containsAllInOrder(expected.toList()..sort()));
@@ -76,17 +68,8 @@ void main() {
     var actual = Set<String>.from(meta.findReservedNames());
 
     var expected = findMemberNames(meta.importFrom, #PbEventMixin)
-      ..removeAll(GeneratedMessage_reservedNames)
-      // TODO: see https://github.com/google/protobuf.dart/issues/527
-      ..removeAll(_oneOffNames);
+      ..removeAll(GeneratedMessage_reservedNames);
 
     expect(actual.toList()..sort(), equals(expected.toList()..sort()));
   });
 }
-
-// See https://github.com/google/protobuf.dart/issues/527
-const _oneOffNames = {
-  'hash',
-  'hashAll',
-  'hashAllUnordered',
-};