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

Analysis hole: attributes on instance methods in RUC types #3140

Open
Tracked by #101149
sbomer opened this issue Nov 28, 2022 · 8 comments
Open
Tracked by #101149

Analysis hole: attributes on instance methods in RUC types #3140

sbomer opened this issue Nov 28, 2022 · 8 comments
Assignees
Milestone

Comments

@sbomer
Copy link
Member

sbomer commented Nov 28, 2022

RUC on type silences warnings from instance methods, but those instance methods may have attributes that should produce warnings. We incorrectly treat RUC on type as suppressing those warnings, even though reflection access to those methods can call the attribute ctor at runtime. Example:

class RequiresOnAttributeCtorAttribute : Attribute
{
    [RUC("--ATTRIBUTE CTOR--")]
    public RequiresOnAttributeCtorAttribute()
    {
        Console.WriteLine("ATTRIBUTE CTOR CALLED!");
    }
}

[RequiresUnreferencedCode("--TypeWithRequires--")]
class TypeWithRequires
{
    [RequiresOnAttributeCtor]
    public void InstanceMethod() { }
}

public static void Test()
{
    foreach (var method in typeof(TypeWithRequires).GetMethods(BindingFlags.Public | BindingFlags.Instance))
    {
        foreach (var a in method.GetCustomAttributes()) ;
    }
}
@MichalStrehovsky
Copy link
Member

In similar spirit, this one won't warn either. Maybe the problem is that we don't warn that the method is reflection-accessed?

using System;
using System.Diagnostics.CodeAnalysis;

var del = typeof(Foo).GetMethod(nameof(Foo.Get)).CreateDelegate<Func<Foo, string, Type>>();
var t = del(null, Console.ReadLine());
Console.WriteLine(t);

[RequiresUnreferencedCode("Don't use")]
class Foo
{
    public Type Get(string s) => Type.GetType(s);
}

@sbomer
Copy link
Member Author

sbomer commented Nov 29, 2022

Thanks for pointing that out, I didn't realize that reflection-invoke could call an instance method on null without throwing a NRE.

@MichalStrehovsky
Copy link
Member

One can also do the same with an IL call instruction. We just rely on the fact that C# mandates calling instance method with a null this throw NRE and Roslyn therefore uses callvirt for all instance method calls where it cannot prove they're not null.

(Note that calling instance method with a null this has issues and people should only do it as a party trick, if that's the kind of parties they go to.)

@sbomer
Copy link
Member Author

sbomer commented Dec 1, 2022

I am working on a change that will warn on reflection access to instance methods of a RUC type to address the above.

However, instance fields still have the same problem with attributes that methods have. @MichalStrehovsky do you think it would make sense to warn on reflection access to instance fields of a RUC type as well, to cover this? I couldn't think of any independent justification for doing so like we have for methods.

@sbomer
Copy link
Member Author

sbomer commented Dec 1, 2022

Maybe it would be best to limit the warnings to fields which have RUC attribute instances on them. Otherwise compiler-generated code for RUC user methods will have lots of generated fields that are logically in a RUC scope but will never have any problematic attribute instances on them.

@sbomer sbomer self-assigned this Dec 1, 2022
@MichalStrehovsky
Copy link
Member

However, instance fields still have the same problem with attributes that methods have. @MichalStrehovsky do you think it would make sense to warn on reflection access to instance fields of a RUC type as well, to cover this?

Should RUC on a type apply to instance fields in the first place? RUC is about behaviors, not storage locations. IIRC the only reason why RUC does something with fields at all is that accessing static fields triggers the cctor and that's a behavior (I didn't fully agree on that one and would have preferred RUC on type to not apply to cctor - to basically have no way to mark a cctor as RUC). But accessing instance fields can't trigger a cctor. I can't come up with a situation where it wouldn't be safe.

@sbomer
Copy link
Member Author

sbomer commented Dec 1, 2022

Maybe not - that's kind of what I'm trying to decide. I agree that we originally decided RUC on type would only apply to static fields because they could trigger the cctor.

The only situation I can come up with where it would be unsafe to reflection-access an instance field is where the field has attributes with a RUC constructor, so I'm wondering what the warning location should be for that scenario - whether it's the attribute instance, or reflection-access to the field. Since we are saying that attributes on methods in RUC types don't warn (being "covered" by the reflection-access warnings), it just feels inconsistent for them to warn on instance fields. And it feels especially inconsistent if they don't warn on static fields.

I'm fixing the hole for methods in #3147, but there I'm leaving instance fields alone until we decide on the behavior.

@sbomer
Copy link
Member Author

sbomer commented Dec 1, 2022

Another thing to consider is that the reflection-access-to-RUC-member warnings will all have the same warning code, so suppressing one of them will suppress all of them. This is good and bad - it means that somebody who is sure they're not doing anything "unsafe" doesn't need to worry about suppressing for each potentially accessed member, but also that suppressed warnings could hide problems if new RUC members are introduced. For the instance field scenario, I think it means that the extra warnings aren't too troublesome - anyone reflecting over a RUC type probably won't need extra suppressions for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants