Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new modifier to class methods that hide base class methods #1097

Merged
merged 1 commit into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
badcel marked this conversation as resolved.
Show resolved Hide resolved
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");
}
}
Loading