From 47f2017cf3880821727deb5c12c9b850b01fca9c Mon Sep 17 00:00:00 2001
From: Sydney Acksman <obsidianminor@gmail.com>
Date: Tue, 2 Jul 2019 18:06:55 -0500
Subject: [PATCH] Review changes  * Add Syntax enum and make file syntax checks
 check the enum  * Made IsPacked a normal get method without ternary op  *
 Moved IFieldAccessor.HasValue to IFieldPresenceAccessor  * Change naming of
 GetIsExtensionsInitialized  * Fixed stray text in summary text

---
 .../Reflection/FieldAccessTest.cs             |  4 +-
 .../src/Google.Protobuf/MessageExtensions.cs  |  8 ++--
 .../Reflection/FieldDescriptor.cs             | 15 +++++++-
 .../Reflection/FileDescriptor.cs              | 37 +++++++++++++++++++
 .../Reflection/IFieldAccessor.cs              | 16 +++++---
 .../Reflection/MessageDescriptor.cs           |  2 +-
 .../Reflection/ReflectionUtil.cs              |  2 +-
 .../Reflection/SingleFieldAccessor.cs         |  4 +-
 8 files changed, 72 insertions(+), 16 deletions(-)

diff --git a/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs
index fcfff4f7ac..dd753e8df4 100644
--- a/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs
@@ -101,7 +101,7 @@ namespace Google.Protobuf.Reflection
         {
             IMessage message = SampleMessages.CreateFullTestAllTypes();
             var fields = message.Descriptor.Fields;
-            Assert.Throws<InvalidOperationException>(() => fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.HasValue(message));
+            Assert.Throws<InvalidOperationException>(() => (fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor as IFieldPresenceAccessor).HasValue(message));
         }
 
         [Test]
@@ -109,7 +109,7 @@ namespace Google.Protobuf.Reflection
         {
             IMessage message = new Proto2.TestAllTypes();
             var fields = message.Descriptor.Fields;
-            var accessor = fields[Proto2.TestAllTypes.OptionalBoolFieldNumber].Accessor;
+            var accessor = fields[Proto2.TestAllTypes.OptionalBoolFieldNumber].Accessor as IFieldPresenceAccessor;
 
             Assert.False(accessor.HasValue(message));
 
diff --git a/csharp/src/Google.Protobuf/MessageExtensions.cs b/csharp/src/Google.Protobuf/MessageExtensions.cs
index d297a118c5..986c3798dd 100644
--- a/csharp/src/Google.Protobuf/MessageExtensions.cs
+++ b/csharp/src/Google.Protobuf/MessageExtensions.cs
@@ -148,12 +148,12 @@ namespace Google.Protobuf
         /// </summary>
         public static bool IsInitialized(this IMessage message)
         {
-            if (message.Descriptor.File.Proto.Syntax == "proto3")
+            if (message.Descriptor.File.Syntax == Syntax.Proto3)
             {
                 return true;
             }
 
-            if (!message.Descriptor.GetIsExtensionsInitialized(message))
+            if (!message.Descriptor.IsExtensionsInitialized(message))
             {
                 return false;
             }
@@ -183,7 +183,7 @@ namespace Google.Protobuf
                     }
                     else if (f.FieldType == FieldType.Message || f.FieldType == FieldType.Group)
                     {
-                        if (f.Accessor.HasValue(message))
+                        if ((f.Accessor as IFieldPresenceAccessor).HasValue(message))
                         {
                             return ((IMessage)f.Accessor.GetValue(message)).IsInitialized();
                         }
@@ -194,7 +194,7 @@ namespace Google.Protobuf
                     }
                     else if (f.IsRequired)
                     {
-                        return f.Accessor.HasValue(message);
+                        return (f.Accessor as IFieldPresenceAccessor).HasValue(message);
                     }
                     else
                     {
diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
index 807e6ce0d8..a8b9e69f44 100644
--- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
@@ -201,7 +201,20 @@ namespace Google.Protobuf.Reflection
         /// <summary>
         /// Returns <c>true</c> if this field is a packed, repeated field; <c>false</c> otherwise.
         /// </summary>
-        public bool IsPacked => File.Proto.Syntax != "proto3" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;
+        public bool IsPacked
+        {
+            get
+            {
+                if (File.Syntax != Syntax.Proto3)
+                {
+                    return Proto.Options?.Packed ?? false;
+                }
+                else
+                {
+                    return !Proto.Options.HasPacked || Proto.Options.Packed;
+                }
+            }
+        }
 
         /// <summary>
         /// Returns <c>true</c> if this field extends another message type; <c>false</c> otherwise.
diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
index 13266f725d..993f428cab 100644
--- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
@@ -42,6 +42,25 @@ using static Google.Protobuf.Reflection.SourceCodeInfo.Types;
 
 namespace Google.Protobuf.Reflection
 {
+    /// <summary>
+    /// The syntax of a .proto file
+    /// </summary>
+    public enum Syntax
+    {
+        /// <summary>
+        /// Proto2 syntax
+        /// </summary>
+        Proto2,
+        /// <summary>
+        /// Proto3 syntax
+        /// </summary>
+        Proto3,
+        /// <summary>
+        /// An unknown declared syntax
+        /// </summary>
+        Unknown
+    }
+
     /// <summary>
     /// Describes a .proto file, including everything defined within.
     /// IDescriptor is implemented such that the File property returns this descriptor,
@@ -87,6 +106,19 @@ namespace Google.Protobuf.Reflection
             Extensions = new ExtensionCollection(this, generatedCodeInfo?.Extensions);
 
             declarations = new Lazy<Dictionary<IDescriptor, DescriptorDeclaration>>(CreateDeclarationMap, LazyThreadSafetyMode.ExecutionAndPublication);
+
+            if (!proto.HasSyntax || proto.Syntax == "proto2")
+            {
+                Syntax = Syntax.Proto2;
+            }
+            else if (proto.Syntax == "proto3")
+            {
+                Syntax = Syntax.Proto3;
+            }
+            else
+            {
+                Syntax = Syntax.Unknown;
+            }
         }
 
         private Dictionary<IDescriptor, DescriptorDeclaration> CreateDeclarationMap()
@@ -217,6 +249,11 @@ namespace Google.Protobuf.Reflection
         /// </value>
         internal FileDescriptorProto Proto { get; }
 
+        /// <summary>
+        /// The syntax of the file
+        /// </summary>
+        public Syntax Syntax { get; }
+
         /// <value>
         /// The file name.
         /// </value>
diff --git a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs
index 8f7ef3c67c..373984e9b1 100644
--- a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs
@@ -51,11 +51,6 @@ namespace Google.Protobuf.Reflection
         /// </summary>
         void Clear(IMessage message);
 
-        /// <summary>
-        /// Indicates whether the field in the specified message is set. For proto3 fields, this throws an <see cref="InvalidOperationException"/>
-        /// </summary>
-        bool HasValue(IMessage message);
-
         /// <summary>
         /// Fetches the field value. For repeated values, this will be an
         /// <see cref="IList"/> implementation. For map values, this will be an
@@ -73,4 +68,15 @@ namespace Google.Protobuf.Reflection
         /// <exception cref="InvalidOperationException">The field is not a "simple" field.</exception>
         void SetValue(IMessage message, object value);
     }
+
+    /// <summary>
+    /// Allows field presence to be checked reflectively. This is implemented for all single field accessors
+    /// </summary>
+    public interface IFieldPresenceAccessor : IFieldAccessor
+    {
+        /// <summary>
+        /// Indicates whether the field in the specified message is set. For proto3 fields, this throws an <see cref="InvalidOperationException"/>
+        /// </summary>
+        bool HasValue(IMessage message);
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
index a6ecfb984b..6a93a8faa8 100644
--- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
@@ -136,7 +136,7 @@ namespace Google.Protobuf.Reflection
 
         internal DescriptorProto Proto { get; }
 
-        internal bool GetIsExtensionsInitialized(IMessage message)
+        internal bool IsExtensionsInitialized(IMessage message)
         {
             if (Proto.ExtensionRange.Count == 0)
             {
diff --git a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
index 5e66b6f2e4..28f724b2d8 100644
--- a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
+++ b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
@@ -71,7 +71,7 @@ namespace Google.Protobuf.Reflection
 
         /// <summary>
         /// Empty Type[] used when calling GetProperty to force property instead of indexer fetching.
-        /// </summary>getFieldFunc
+        /// </summary>
         internal static readonly Type[] EmptyTypes = new Type[0];
 
         /// <summary>
diff --git a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs
index dc6e0ca9ae..d5a8e6ff7d 100644
--- a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs
@@ -39,7 +39,7 @@ namespace Google.Protobuf.Reflection
     /// <summary>
     /// Accessor for single fields.
     /// </summary>
-    internal sealed class SingleFieldAccessor : FieldAccessorBase
+    internal sealed class SingleFieldAccessor : FieldAccessorBase, IFieldPresenceAccessor
     {
         // All the work here is actually done in the constructor - it creates the appropriate delegates.
         // There are various cases to consider, based on the property type (message, string/bytes, or "genuine" primitive)
@@ -57,7 +57,7 @@ namespace Google.Protobuf.Reflection
                 throw new ArgumentException("Not all required properties/methods available");
             }
             setValueDelegate = ReflectionUtil.CreateActionIMessageObject(property.GetSetMethod());
-            if (descriptor.File.Proto.Syntax == "proto3")
+            if (descriptor.File.Syntax == Syntax.Proto3)
             {
                 hasDelegate = message => {
                   throw new InvalidOperationException("HasValue is not implemented for proto3 fields");