Skip to content

Commit

Permalink
Add new modifier to class methods that hide base class methods
Browse files Browse the repository at this point in the history
  • Loading branch information
adamreeve committed Aug 4, 2024
1 parent 4ba4543 commit 9ba6c1c
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 4 deletions.
19 changes: 15 additions & 4 deletions src/Generation/Generator/Model/Function.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,21 @@ public static string GetImportResolver(GirModel.Function function)

public static bool HidesFunction(GirModel.Function function)
{
if (function.Parent is not GirModel.Class cls)
return false;

return HidesFunction(cls.Parent, function);
switch (function.Parent)
{
case GirModel.Class cls:
return HidesFunction(cls.Parent, function);
case GirModel.Interface @interface:
{
// Rendering a function for an interface helper class,
// which inherits from GObject.Object so hides GetType.
// This workaround could be replaced by using a static interface method
// for GetType when dotnet 6 support is dropped.
return function == @interface.TypeFunction;
}
default:
return false;
}
}

private static bool HidesFunction(GirModel.Class? cls, GirModel.Function function)
Expand Down
77 changes: 77 additions & 0 deletions src/Generation/Generator/Model/Method.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Generator.Model;

Expand Down Expand Up @@ -84,4 +85,80 @@ public static bool IsValidFreeFunction(GirModel.Method method)
{
return !method.Parameters.Any() && method.ReturnType.AnyType.TryPickT0(out var type, out _) && type is GirModel.Void;
}

/// <summary>
/// Whether this method hides a method with the same name and parameters from a parent class
/// </summary>
public static bool HidesMethod(GirModel.Method method)
{
if (method.Parent is not GirModel.Class cls)
{
return false;
}

var publicName = GetPublicName(method);
return HidesMethod(cls.Parent, method, publicName);
}

private static bool HidesMethod(GirModel.Class? cls, GirModel.Method method, string publicName)
{
if (cls is null)
{
return HidesObjectMethod(method, publicName);
}

var matchingMethod = cls.Methods.FirstOrDefault(m => GetPublicName(m) == publicName);

if (matchingMethod is null)
{
return HidesMethod(cls.Parent, method, publicName);
}

GirModel.Parameter[] parameters = method.Parameters.ToArray();
GirModel.Parameter[] foundParameters = matchingMethod.Parameters.ToArray();

if (parameters.Length != foundParameters.Length)
{
return HidesMethod(cls.Parent, method, publicName);
}

for (var i = 0; i < parameters.Length; i++)
{
if (!parameters[i].AnyTypeOrVarArgs.Equals(foundParameters[i].AnyTypeOrVarArgs))
{
return HidesMethod(cls.Parent, method, publicName);
}
}

return true;
}

/// <summary>
/// Whether this method hides a method from System.Object
/// </summary>
private static bool HidesObjectMethod(GirModel.Method method, string publicName)
{
if (method.Parameters.Any())
{
// We do not currently support detecting overrides of object methods that accept parameters
return false;
}

var objectType = typeof(object);
var matchingMembers = objectType.GetMember(
publicName, BindingFlags.Instance | BindingFlags.Public);
foreach (var matchingMember in matchingMembers)
{
if (matchingMember is MethodBase matchingMethod)
{
var parameters = matchingMethod.GetParameters();
if (parameters.Length == 0)
{
return true;
}
}
}

return false;
}
}
5 changes: 5 additions & 0 deletions src/Generation/Generator/Renderer/Public/MethodRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public static string Render(GirModel.Method method)
try
{
var modifier = "public ";
if (Method.HidesMethod(method))
{
modifier += "new ";
}

var explicitImplementation = string.Empty;
if (Method.GetImplemnetExplicitly(method))
{
Expand Down
1 change: 1 addition & 0 deletions src/Libs/GirTest-0.1/GirTest-0.1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<PropertyGroup>
<IsPackable>false</IsPackable>
<WarningsAsErrors>CS0108,CS0114</WarningsAsErrors>
</PropertyGroup>

<ItemGroup>
Expand Down
73 changes: 73 additions & 0 deletions src/Native/GirTestLib/girtest-method-hiding.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "girtest-method-hiding.h"

/**
* GirTestMethodHidingBase:
*
* Base class for testing subclasses with method names that are the same as a parent class method
*/

G_DEFINE_TYPE(GirTestMethodHidingBase, girtest_method_hiding_base, G_TYPE_OBJECT)

static void
girtest_method_hiding_base_init(GirTestMethodHidingBase *value)
{
}

static void
girtest_method_hiding_base_class_init(GirTestMethodHidingBaseClass *class)
{
}

gchar *
girtest_method_hiding_base_to_string(GirTestMethodHidingBase *instance)
{
return g_strdup("New to_string");
}

gchar *
girtest_method_hiding_base_custom_string(GirTestMethodHidingBase *instance)
{
return g_strdup("Base class custom string");
}

/**
* GirTestMethodHidingSubclass:
*
* Subclass for testing method names that are the same as a parent class method
*/

struct _GirTestMethodHidingSubclass
{
GirTestMethodHidingBase parent_instance;
};

G_DEFINE_TYPE(GirTestMethodHidingSubclass, girtest_method_hiding_subclass, GIRTEST_TYPE_METHOD_HIDING_BASE)

static void
girtest_method_hiding_subclass_init(GirTestMethodHidingSubclass *value)
{
}

static void
girtest_method_hiding_subclass_class_init(GirTestMethodHidingSubclassClass *class)
{
}

/**
* girtest_method_hiding_subclass_new:
*
* Creates a new `GirTestMethodHidingSubclass`.
*
* Returns: The newly created `MethodHidingSubclass`.
*/
GirTestMethodHidingSubclass*
girtest_method_hiding_subclass_new(void)
{
return g_object_new (GIRTEST_TYPE_METHOD_HIDING_SUBCLASS, NULL);
}

gchar *
girtest_method_hiding_subclass_custom_string(GirTestMethodHidingSubclass *instance)
{
return g_strdup("Subclass custom string");
}
35 changes: 35 additions & 0 deletions src/Native/GirTestLib/girtest-method-hiding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <glib-object.h>

G_BEGIN_DECLS

#define GIRTEST_TYPE_METHOD_HIDING_BASE girtest_method_hiding_base_get_type()
G_DECLARE_DERIVABLE_TYPE(GirTestMethodHidingBase, girtest_method_hiding_base, GIRTEST, METHOD_HIDING_BASE, GObject)

struct _GirTestMethodHidingBaseClass
{
GObjectClass parent_class;
};

gchar *
girtest_method_hiding_base_to_string(GirTestMethodHidingBase *instance);

gchar *
girtest_method_hiding_base_custom_string(GirTestMethodHidingBase *instance);

#define GIRTEST_TYPE_METHOD_HIDING_SUBCLASS girtest_method_hiding_subclass_get_type()
G_DECLARE_FINAL_TYPE(GirTestMethodHidingSubclass, girtest_method_hiding_subclass, GIRTEST, METHOD_HIDING_SUBCLASS, GirTestMethodHidingBase)

struct _GirTestMethodHidingSubclassClass
{
GirTestMethodHidingBaseClass parent_class;
};

GirTestMethodHidingSubclass*
girtest_method_hiding_subclass_new(void);

gchar *
girtest_method_hiding_subclass_custom_string(GirTestMethodHidingSubclass *instance);

G_END_DECLS
2 changes: 2 additions & 0 deletions src/Native/GirTestLib/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ header_files = [
'girtest-error-tester.h',
'girtest-integer-array-tester.h',
'girtest-long-tester.h',
'girtest-method-hiding.h',
'girtest-opaque-typed-record-copy-annotation-tester.h',
'girtest-opaque-typed-record-tester.h',
'girtest-opaque-untyped-record-tester.h',
Expand Down Expand Up @@ -44,6 +45,7 @@ source_files = [
'girtest-error-tester.c',
'girtest-integer-array-tester.c',
'girtest-long-tester.c',
'girtest-method-hiding.c',
'girtest-opaque-typed-record-copy-annotation-tester.c',
'girtest-opaque-typed-record-tester.c',
'girtest-opaque-untyped-record-tester.c',
Expand Down
38 changes: 38 additions & 0 deletions src/Tests/Libs/GirTest-0.1.Tests/MethodHidingTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GirTest.Tests;

[TestClass, TestCategory("BindingTest")]
public class MethodHidingTest : Test
{
[TestMethod]
public void CanCallNewToStringMethod()
{
var instance = MethodHidingSubclass.New();
instance.ToString().Should().Be("New to_string");
}

[TestMethod]
public void CanCallObjectToStringMethod()
{
var instance = MethodHidingSubclass.New();
var asObj = (object) instance;
asObj.ToString().Should().Contain("MethodHidingSubclass");
}

[TestMethod]
public void CanCallNewMethodOnSubclass()
{
var instance = MethodHidingSubclass.New();
instance.CustomString().Should().Be("Subclass custom string");
}

[TestMethod]
public void CanCallNewMethodFromBaseClass()
{
var instance = MethodHidingSubclass.New();
var asBase = ((MethodHidingBase) instance);
asBase.CustomString().Should().Be("Base class custom string");
}
}

0 comments on commit 9ba6c1c

Please sign in to comment.