lauromoura pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=b1eb794a913e753929635aa80aeab8c2211db462

commit b1eb794a913e753929635aa80aeab8c2211db462
Author: Lauro Moura <[email protected]>
Date:   Tue Nov 19 01:48:01 2019 -0300

    csharp: Fix CA1815 for generated structs and aliases
    
    Summary: Adds IEquatable<T> and friends.
    
    Reviewers: felipealmeida, YOhoho, brunobelo
    
    Reviewed By: brunobelo
    
    Subscribers: cedric, #reviewers, segfaultxavi, #committers
    
    Tags: #efl
    
    Maniphest Tasks: T8418
    
    Differential Revision: https://phab.enlightenment.org/D10694
---
 .../eolian_mono/eolian/mono/alias_definition.hh    |  61 ++++++++++-
 src/bin/eolian_mono/eolian/mono/name_helpers.hh    |  20 ++++
 .../eolian_mono/eolian/mono/struct_definition.hh   | 114 ++++++++++++++++++++-
 src/tests/efl_mono/Eo.cs                           |  36 +++++++
 src/tests/efl_mono/Structs.cs                      |  44 ++++++++
 5 files changed, 271 insertions(+), 4 deletions(-)

diff --git a/src/bin/eolian_mono/eolian/mono/alias_definition.hh 
b/src/bin/eolian_mono/eolian/mono/alias_definition.hh
index 7f3f2588f3..31d86fe10d 100644
--- a/src/bin/eolian_mono/eolian/mono/alias_definition.hh
+++ b/src/bin/eolian_mono/eolian/mono/alias_definition.hh
@@ -59,7 +59,7 @@ struct alias_definition_generator
      std::string const alias_name = utils::remove_all(alias.eolian_name, '_');
      if (!as_generator(
                  documentation
-                 << "public struct " << alias_name << "\n"
+                 << "public struct " << alias_name << " : IEquatable<" << 
alias_name << ">\n"
                  << "{\n"
                  << scope_tab << "private " << alias_type << " payload;\n\n"
 
@@ -93,7 +93,64 @@ struct alias_definition_generator
                  << scope_tab << "{\n"
                  << scope_tab << scope_tab << "return this;\n"
                  << scope_tab << "}\n"
-                 << "}\n"
+                 ).generate(sink, alias, context))
+       return false;
+
+     std::string since_line;
+     if (!alias.documentation.since.empty())
+         if (!as_generator(scope_tab << "/// <para>Since EFL " + 
alias.documentation.since + ".</para>\n"
+                 ).generate(std::back_inserter(since_line), 
attributes::unused, context))
+           return false;
+
+     // GetHashCode (needed by the equality comparisons)
+     if (!as_generator(
+             scope_tab << "/// <summary>Get a hash code for this item.\n"
+             << since_line
+             << scope_tab << "/// </summary>\n"
+             << scope_tab << "public override int GetHashCode() => 
payload.GetHashCode();\n"
+          ).generate(sink, attributes::unused, context))
+       return false;
+
+     // IEquatble<T> Equals
+     if (!as_generator(
+                 scope_tab << "/// <summary>Equality comparison.\n"
+                 << since_line
+                 << scope_tab << "/// </summary>\n"
+                 << scope_tab << "public bool Equals(" << alias_name << " 
other) => payload == other.payload;\n"
+          ).generate(sink, attributes::unused, context))
+       return false;
+
+     // ValueType.Equals
+     if (!as_generator(
+                 scope_tab << "/// <summary>Equality comparison.\n"
+                 << since_line
+                 << scope_tab << "/// </summary>\n"
+                 << scope_tab << "public override bool Equals(object other)\n"
+                 << scope_tab << scope_tab << "=> ((other is " << alias_name 
<< ") ? Equals((" << alias_name << ")other) : false);\n"
+        ).generate(sink, attributes::unused, context))
+       return false;
+
+     // Equality operators
+     if (!as_generator(
+                 scope_tab << "/// <summary>Equality comparison.\n"
+                 << since_line
+                 << scope_tab << "/// </summary>\n"
+                 << scope_tab << "public static bool operator ==(" << 
alias_name << " lhs, " << alias_name << " rhs)\n"
+                 << scope_tab << scope_tab << "=> lhs.payload == 
rhs.payload;\n"
+        ).generate(sink, attributes::unused, context))
+       return false;
+
+     if (!as_generator(
+                 scope_tab << "/// <summary>Equality comparison.\n"
+                 << since_line
+                 << scope_tab << "/// </summary>\n"
+                 << scope_tab << "public static bool operator !=(" << 
alias_name << " lhs, " << alias_name << " rhs)\n"
+                 << scope_tab << scope_tab << "=> lhs.payload != 
rhs.payload;\n"
+        ).generate(sink, attributes::unused, context))
+       return false;
+
+     if (!as_generator(
+                 "}\n"
                  ).generate(sink, alias, context))
        return false;
 
diff --git a/src/bin/eolian_mono/eolian/mono/name_helpers.hh 
b/src/bin/eolian_mono/eolian/mono/name_helpers.hh
index 2f3026dfdc..a3ffe47a3b 100644
--- a/src/bin/eolian_mono/eolian/mono/name_helpers.hh
+++ b/src/bin/eolian_mono/eolian/mono/name_helpers.hh
@@ -563,6 +563,17 @@ std::string translate_value_type(std::string const& name)
   return name;
 }
 
+
+// Field names //
+struct struct_field_name_generator
+{
+  template <typename OutputIterator, typename Context>
+  bool generate(OutputIterator sink, attributes::struct_field_def const& 
field, Context const& context) const
+  {
+    return as_generator(string).generate(sink, 
name_helpers::to_field_name(field.name), context);
+  }
+} const struct_field_name {};
+
 } // namespace name_helpers
 
 } // namespace eolian_mono
@@ -590,9 +601,18 @@ struct 
is_eager_generator<eolian_mono::name_helpers::klass_full_concrete_name_ge
 template <>
 struct 
is_generator<eolian_mono::name_helpers::klass_full_concrete_name_generator> : 
std::true_type {};
 
+template <>
+struct 
is_eager_generator<eolian_mono::name_helpers::struct_field_name_generator> : 
std::true_type {};
+template <>
+struct is_generator< ::eolian_mono::name_helpers::struct_field_name_generator> 
: std::true_type {};
+
 namespace type_traits {
 template <>
 struct attributes_needed<struct 
::eolian_mono::name_helpers::klass_full_concrete_or_interface_name_generator> : 
std::integral_constant<int, 1> {};
+
+template <>
+struct attributes_needed< 
::eolian_mono::name_helpers::struct_field_name_generator> : 
std::integral_constant<int, 1> {};
+
 }
       
 } } }
diff --git a/src/bin/eolian_mono/eolian/mono/struct_definition.hh 
b/src/bin/eolian_mono/eolian/mono/struct_definition.hh
index b3b8d717f6..7c0bc9e4e1 100644
--- a/src/bin/eolian_mono/eolian/mono/struct_definition.hh
+++ b/src/bin/eolian_mono/eolian/mono/struct_definition.hh
@@ -21,6 +21,7 @@
 #include "grammar/indentation.hpp"
 #include "grammar/list.hpp"
 #include "grammar/alternative.hpp"
+#include "grammar/attribute_reorder.hpp"
 #include "name_helpers.hh"
 #include "helpers.hh"
 #include "type.hh"
@@ -408,14 +409,15 @@ struct struct_definition_generator
      auto const& indent = current_indentation(context);
      if(!as_generator(documentation).generate(sink, struct_, context))
        return false;
+     auto struct_managed_name = binding_struct_name(struct_);
      if(!as_generator
         (
             indent << "[StructLayout(LayoutKind.Sequential)]\n"
          << indent << "[Efl.Eo.BindingEntity]\n"
-         << indent << "public struct " << string << "\n"
+         << indent << "public struct " << struct_managed_name << " : 
IEquatable<" << struct_managed_name << ">\n"
          << indent << "{\n"
          )
-        .generate(sink, binding_struct_name(struct_), context))
+        .generate(sink, attributes::unused, context))
        return false;
 
      // iterate struct fields
@@ -472,6 +474,114 @@ struct struct_definition_generator
               return false;
        }
 
+     std::string since_line;
+     if (!struct_.documentation.since.empty())
+         if (!as_generator(indent << scope_tab << "/// <para>Since EFL " + 
struct_.documentation.since + ".</para>\n"
+                 ).generate(std::back_inserter(since_line), 
attributes::unused, context))
+           return false;
+
+     // GetHashCode (needed by the equality comparisons)
+     if (!as_generator(
+             indent << scope_tab << "/// <summary>Get a hash code for this 
item.\n"
+             << since_line
+             << indent << scope_tab << "/// </summary>\n"
+             << indent << scope_tab << "public override int GetHashCode()\n"
+             << indent << scope_tab << "{\n"
+          ).generate(sink, attributes::unused, context))
+       return false;
+
+     if (struct_.fields.size() != 0 )
+       {
+          // int hash = 17;
+          // hash = 23 * fieldA.GetHashCode();
+          // hash = 23 * fieldB.GetHashCode();
+          // hash = 23 * fieldC.GetHashCode();
+          // return hash
+          if (!as_generator(
+                indent << scope_tab << scope_tab << "int hash = 17;\n"
+                << *(indent << scope_tab << scope_tab << "hash = hash * 23 + " 
<< name_helpers::struct_field_name << ".GetHashCode();\n")
+                << indent << scope_tab << scope_tab << "return hash;\n"
+              ).generate(sink, struct_.fields, context))
+            return false;
+       }
+     else
+       {
+          // Just compare the place holder pointers
+          if (!as_generator(
+                "return field.GetHashCode();\n"
+              ).generate(sink, attributes::unused, context))
+            return false;
+       }
+
+     if (!as_generator(
+             indent << scope_tab << "}\n"
+          ).generate(sink, attributes::unused, context))
+       return false;
+
+     // IEquatable<T> Equals
+     if (!as_generator(
+             indent << scope_tab << "/// <summary>Equality comparison.\n"
+             << since_line
+             << indent << scope_tab << "/// </summary>\n"
+             << indent << scope_tab << "public bool Equals(" << 
struct_managed_name << " other)\n"
+             << indent << scope_tab << "{\n"
+             << indent << scope_tab << scope_tab << "return "
+          ).generate(sink, attributes::unused, context))
+       return false;
+
+     if (struct_.fields.size() != 0 )
+       {
+          if (!as_generator(
+                grammar::attribute_reorder<-1, 
-1>((name_helpers::struct_field_name << " == other." << 
name_helpers::struct_field_name)) % " && "
+              ).generate(sink, struct_.fields, context))
+            return false;
+       }
+     else
+       {
+          // Just compare the place holder pointers
+          if (!as_generator(
+                "field.Equals(other.field)"
+              ).generate(sink, attributes::unused, context))
+            return false;
+       }
+     
+     
+     if (!as_generator(   
+             indent << scope_tab << scope_tab << ";\n"
+             << indent << scope_tab << "}\n"
+          ).generate(sink, attributes::unused, context))
+      return false;
+
+     // ValueType.Equals
+     if (!as_generator(
+           indent << scope_tab << "/// <summary>Equality comparison.\n"
+           << since_line
+           << indent << scope_tab << "/// </summary>\n"
+           << indent << scope_tab << "public override bool Equals(object 
other)\n"
+           << indent << scope_tab << scope_tab << "=> ((other is " << 
struct_managed_name  << ") ? Equals((" << struct_managed_name << ")other) : 
false);\n"
+        ).generate(sink, attributes::unused, context))
+       return false;
+
+     // Equality operators
+     if (!as_generator(
+           indent << scope_tab << "/// <summary>Equality comparison.\n"
+           << since_line
+           << indent << scope_tab << "/// </summary>\n"
+           << indent << scope_tab << "public static bool operator ==(" << 
struct_managed_name << " lhs, " << struct_managed_name << " rhs)\n"
+           << indent << scope_tab << scope_tab << "=> lhs.Equals(rhs);"
+        ).generate(sink, attributes::unused, context))
+       return false;
+
+     if (!as_generator(
+           indent << scope_tab << "/// <summary>Equality comparison.\n"
+           << since_line
+           << indent << scope_tab << "/// </summary>\n"
+           << indent << scope_tab << "public static bool operator !=(" << 
struct_managed_name << " lhs, " << struct_managed_name << " rhs)\n"
+           << indent << scope_tab << scope_tab << "=> !lhs.Equals(rhs);"
+        ).generate(sink, attributes::unused, context))
+       return false;
+
+     // Conversions from/to internal struct and IntPtrs
      if(!as_generator(
             indent << scope_tab << "/// <summary>Implicit conversion to the 
managed representation from a native pointer.\n"
             ).generate(sink, attributes::unused, context))
diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs
index 9a52085902..fc3c0d4c86 100644
--- a/src/tests/efl_mono/Eo.cs
+++ b/src/tests/efl_mono/Eo.cs
@@ -675,4 +675,40 @@ class TestHiddenClasses
     }
 }
 
+class TestAliasEquality
+{
+    static Dummy.MyInt a = 4;
+    static Dummy.MyInt b = 4;
+    static Dummy.MyInt c = 5;
+
+    public static void test_equals()
+    {
+        Test.AssertEquals(a, b);
+        Test.AssertNotEquals(a, c);
+    }
+
+    public static void test_equals_different_types()
+    {
+        Test.Assert(!(a.Equals(new Object())));
+    }
+
+    public static void test_equatable()
+    {
+        Test.Assert(((IEquatable<Dummy.MyInt>)a).Equals(b));
+        Test.Assert(!((IEquatable<Dummy.MyInt>)a).Equals(c));
+    }
+
+    public static void test_equality_operators()
+    {
+        Test.Assert(a == b);
+        Test.Assert(a != c);
+    }
+
+    public static void test_hash_code()
+    {
+        Test.AssertEquals(a.GetHashCode(), b.GetHashCode());
+        Test.AssertNotEquals(a.GetHashCode(), c.GetHashCode());
+    }
+}
+
 }
diff --git a/src/tests/efl_mono/Structs.cs b/src/tests/efl_mono/Structs.cs
index 998610c785..60be42eea5 100644
--- a/src/tests/efl_mono/Structs.cs
+++ b/src/tests/efl_mono/Structs.cs
@@ -377,4 +377,48 @@ internal class TestStructs
     // }
 }
 
+internal class TestStructEquality
+{
+    static Dummy.StructSimple a = new Dummy.StructSimple(1, 2, (char)3, 4, 
Fstring: "", Fmstring: "", Fstringshare: "");
+    static Dummy.StructSimple b = new Dummy.StructSimple(1, 2, (char)3, 4, 
Fstring: "", Fmstring: "", Fstringshare: "");
+
+    static Dummy.StructSimple c = new Dummy.StructSimple(4, 3, (char)2, 1, 
Fstring: "", Fmstring: "", Fstringshare: "");
+
+    // to check if we differ on a single struct field
+    static Dummy.StructSimple singleDifferentField = new Dummy.StructSimple(1, 
2, (char)3, 5, Fstring: "", Fmstring: "", Fstringshare: "");
+
+    public static void test_equals()
+    {
+        Test.AssertEquals(a, b);
+        Test.AssertNotEquals(a, c);
+        Test.AssertNotEquals(a, singleDifferentField);
+    }
+
+    public static void test_equals_different_types()
+    {
+        Test.Assert(!(a.Equals(new Object())));
+    }
+
+    public static void test_equatable()
+    {
+        Test.Assert(((IEquatable<Dummy.StructSimple>)a).Equals(b));
+        Test.Assert(!((IEquatable<Dummy.StructSimple>)a).Equals(c));
+        
Test.Assert(!((IEquatable<Dummy.StructSimple>)a).Equals(singleDifferentField));
+    }
+
+    public static void test_equality_operators()
+    {
+        Test.Assert(a == b);
+        Test.Assert(a != c);
+        Test.Assert(a != singleDifferentField);
+    }
+
+    public static void test_hash_code()
+    {
+        Test.AssertEquals(a.GetHashCode(), b.GetHashCode());
+        Test.AssertNotEquals(a.GetHashCode(), c.GetHashCode());
+        Test.AssertNotEquals(a.GetHashCode(), 
singleDifferentField.GetHashCode());
+    }
+}
+
 }

-- 


Reply via email to