diff options
author | Lauro Moura <lauromoura@expertisesolutions.com.br> | 2019-11-19 01:48:01 -0300 |
---|---|---|
committer | Lauro Moura <lauromoura@expertisesolutions.com.br> | 2019-11-19 21:49:35 -0300 |
commit | b1eb794a913e753929635aa80aeab8c2211db462 (patch) | |
tree | d36a71f0f1ba8a08de8d1e3078917088e1d07b59 | |
parent | e36b1930bf04694fb67dd7e741dbcb5231bf5e58 (diff) | |
download | efl-b1eb794a913e753929635aa80aeab8c2211db462.tar.gz |
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
-rw-r--r-- | src/bin/eolian_mono/eolian/mono/alias_definition.hh | 61 | ||||
-rw-r--r-- | src/bin/eolian_mono/eolian/mono/name_helpers.hh | 20 | ||||
-rw-r--r-- | src/bin/eolian_mono/eolian/mono/struct_definition.hh | 114 | ||||
-rw-r--r-- | src/tests/efl_mono/Eo.cs | 36 | ||||
-rw-r--r-- | 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()); + } +} + } |