forked from enlightenment/efl
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
This commit is contained in:
parent
e36b1930bf
commit
b1eb794a91
|
@ -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;
|
||||
|
||||
|
|
|
@ -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> {};
|
||||
|
||||
}
|
||||
|
||||
} } }
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue