Skip to content

System.Reflection.CustomAttributeFormatException thrown on a retrieval of derived attribute with overridden property getter #110945

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

Open
wants to merge 25 commits into
base: main
Choose a base branch
from

Conversation

pedrobsaila
Copy link
Contributor

Fixes #110412

…l of derived attribute with overridden property getter
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Dec 25, 2024
@@ -1580,7 +1580,7 @@ private static void AddCustomAttributes(
RuntimeMethodInfo setMethod = property.GetSetMethod(true)!;

// Public properties may have non-public setter methods
if (!setMethod.IsPublic)
if (setMethod == null || !setMethod.IsPublic)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is null to avoid unnecessary comparison operator

@@ -1580,7 +1580,7 @@ private static void AddCustomAttributes(
RuntimeMethodInfo setMethod = property.GetSetMethod(true)!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nullability suppression should also be removed

Type? baseDeclaredType = declaredType.BaseType;

while ((m_getterMethod == null || m_setterMethod == null)
&& baseDeclaredType != null && baseDeclaredType is RuntimeType baseDeclaredRuntimeType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
&& baseDeclaredType != null && baseDeclaredType is RuntimeType baseDeclaredRuntimeType)
&& baseDeclaredType is RuntimeType baseDeclaredRuntimeType)

The pattern match also does a null check.

Copy link
Contributor

@steveharter steveharter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some test failures for Mono; the changes need to be ported to Mono as well.

out _, out _, out _,
out m_getterMethod, out m_setterMethod, out m_otherMethod,
out isPrivate, out m_bindingFlags);

// lookup getter/setter when getter and setter are inherited from base class but just a setter/getter is overridden on a sub type
if ((m_getterMethod != null && m_getterMethod.IsVirtual && m_setterMethod == null)
Copy link
Contributor

@steveharter steveharter Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: as mentioned earlier, is null and is not null is preferred over == null and != null since it will prevent a call to the == or != operator if that is overridden. In this case, RuntimeMethodInfo doesn't overload those operators so it doesn't matter but it is just more obvious to understand intent when using is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RuntimeMethodInfo doesn't overload those operators so it doesn't matter

MethodInfo does overload those operators. MethodInfo operators are going to be selected to compare RuntimeMethodInfo quick test

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops thanks; updated comment

out _, out _, out _,
out m_getterMethod, out m_setterMethod, out m_otherMethod,
out isPrivate, out m_bindingFlags);

// lookup getter/setter when getter and setter are inherited from base class but just a setter/getter is overridden on a sub type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: start a comment with upper case letter and end with a period.

@pedrobsaila
Copy link
Contributor Author

pedrobsaila commented Dec 29, 2024

There are some test failures for Mono; the changes need to be ported to Mono as well.

I can't get around this error when launching the test in mono windows/linux :

System.TypeLoadException: Could not load type of field 'Xunit.ConsoleClient.ConsoleRunner:completionMessages' (3) due to: Could not load file or assembly 'xunit.runner.utility.netcoreapp10, Version=2.9.2.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c' or one of its dependencies.
  [ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: Could not load type of field 'Xunit.ConsoleClient.ConsoleRunner:completionMessages' (3) due to: Could not load file or assembly 'xunit.runner.utility.netcoreapp10, Version=2.9.2.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c' or one of its dependencies.

The used commands are :

Windows : 
.\build.cmd mono+libs
.\dotnet.cmd build /t:Test C:\OSS\runtime\src\libraries\System.Runtime\tests\System.Runtime.Tests /p:XUnitOptions="-method System.Reflection.Tests.InheritGetterAndSetterMethodInProperty.GetterAndSetterAreAvailableOnSubTypeWhenOverridingOneButInheritingBothFromBaseClass" /p:TargetFramework=net10.0-windows /p:RuntimeFlavor=Mono

Linux :
./build.sh mono+libs
./dotnet.sh build /t:Test ~/OSS/runtime/src/libraries/System.Runtime/tests/System.Runtime.Tests /p:XUnitOptions="-method System.Reflection.Tests.InheritGetterAndSetterMethodInProperty.GetterAndSetterAreAvailableOnSubTypeWhenOverridingOneButInheritingBothFromBaseClass" /p:TargetFramework=net10.0-unix /p:RuntimeFlavor=Mono

When activating debug log, I get :

 Mono: Requesting loading reference 8 (of 13) of C:\Users\badre\.nuget\packages\microsoft.dotnet.xunitconsolerunner\2.9.2-beta.24626.1\tools\net\xunit.con
  sole.dll
  Mono: Loading reference 8 of C:\Users\badre\.nuget\packages\microsoft.dotnet.xunitconsolerunner\2.9.2-beta.24626.1\tools\net\xunit.console.dll (default A
  LC), looking for xunit.runner.utility.netcoreapp10, Version=2.9.2.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c
  Mono: Request to load xunit.runner.utility.netcoreapp10 in alc 000002386BF551F0
  Mono: netcore preload hook: did not find 'xunit.runner.utility.netcoreapp10'.
  Mono: ApplicationBase is C:\OSS\runtime\artifacts\bin\System.Runtime.Tests\Debug\net10.0-windows\
  Mono: Assembly Loader probing location: 'C:\OSS\runtime\artifacts\bin\System.Runtime.Tests\Debug\net10.0-windows\xunit.runner.utility.netcoreapp10.dll'.
  Mono: Image addref xunit.runner.utility.netcoreapp10[00000238705FB810] (default ALC) -> C:\OSS\runtime\artifacts\bin\System.Runtime.Tests\Debug\net10.0-w
  indows\xunit.runner.utility.netcoreapp10.dll[000002387062E790]: 2
  Mono: Predicate: wanted = xunit.runner.utility.netcoreapp10, Version=2.9.2.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c
  Mono: Predicate: candidate = xunit.runner.utility.netcoreapp10, Version=2.9.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c
  Mono: Predicate: candidate and wanted names don't match, returning FALSE
  Mono: Predicate returned FALSE, skipping 'xunit.runner.utility.netcoreapp10' (C:\OSS\runtime\artifacts\bin\System.Runtime.Tests\Debug\net10.0-windows\xun
  it.runner.utility.netcoreapp10.dll)

I see that there is an open issue about this problem in s390x arch #60550 (not sure if it is the same), I have x64

@ericstj
Copy link
Member

ericstj commented Mar 10, 2025

/azp run runtime

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ericstj
Copy link
Member

ericstj commented Mar 10, 2025

Let's see if we can get fresh test results. Previously it looked like there were some linker failures in the build.

@pedrobsaila
Copy link
Contributor Author

pedrobsaila commented Mar 11, 2025

sorry I forgot about this PR, I need to port my changes to nativeaot and fix linker issues. I'll try to resume work on it this week

@pedrobsaila
Copy link
Contributor Author

ready for a new round of reviews

@MichalStrehovsky
Copy link
Member

Before this change, typeof(Derived).GetProperty("P", typeof(int)).SetMethod is null for each of these:

public class Base
{
    public virtual int P { get; set; }
}

public class Derived : Base
{
    public new virtual int P { get => 1; } // NOTE: new
}
public class Base
{
    public int P { get; set; } // NOTE: not even virtual
}

public class Derived : Base
{
    public new virtual int P { get => 1; }
}
public class Base
{
    public uint P { get; set; } // NOTE: uint
}

public class Derived : Base
{
    public new virtual int P { get => 1; }
}

After this change they all return non-null.

This is changing a lot more than a corner case custom attribute issue. I think there are some unresolved design issues that need to be discussed before going to implementation (provided the implementation requires changing how properties worked for 20 years).

The properties in derived classes have been historically considered different things. There's no such thing as an "virtual property". Only the getters/setters are virtual. E.g. how do the proposed changes relate to how this works:

// Prints 0 even though we ask for inherited attributes
Console.WriteLine(typeof(Derived).GetProperty("P", typeof(int)).GetCustomAttributes(inherit: true).Length);

public class Base
{
    [My]
    public virtual int P { get; set; }
}

public class Derived : Base
{
    public override int P { get => 1; }
}

[AttributeUsage(AttributeTargets.All, Inherited = true)]
class MyAttribute : Attribute;

@steveharter steveharter added the needs-author-action An issue or pull request that requires more info or actions from the author. label Apr 25, 2025
@pedrobsaila
Copy link
Contributor Author

Hi @steveharter. I'm waiting here for thre design problems mentionned by @MichalStrehovsky to be addressed before I can continue working on this issue. I can fix the corner case of using new keyword as it is simple but there are other points beside that. Or do you want me to just fix this point ?

@dotnet-policy-service dotnet-policy-service bot removed needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity labels May 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Reflection community-contribution Indicates that the PR has been added by a community member
Projects
None yet
7 participants