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

[API/Analyzer Proposal]: An attribute+analyzer to warn about closure parameters #97151

Open
colejohnson66 opened this issue Jan 18, 2024 · 4 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Milestone

Comments

@colejohnson66
Copy link

Background and motivation

Many times, it is desirable to want a static delegate, not a closure to be passed in. Currently, there is no way to warn against this. Sure, you can put static before the lambda definition to prevent a closure, but that is up to the caller to do so, not the callee.

For example, when writing a source generator, especially an incremental one, many of the delegate parameters should be static, but there is no warning if you accidentally create a closure.

Some analyzers do exist to warn about any closure allocation, such as the heap allocation viewer plugin for JetBrains Rider, but those are indiscriminate in their warnings - any closure is flagged. While useful in many cases, that is not always desired.

API Proposal

As far as the API is concerned, a single attribute would be added to the BCL:

namespace System;

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class DisallowClosuresAttribute : Attribute;

The logic would be implemented by the analyzer.

API Usage

Example usage in the BCL:

namespace System;

public class String
{
    public static string Create<TState>(
        int length,
        TState state,
        [DisallowClosures] SpanAction<char, TState> action); // note the new attribute
}

User experience:

namespace UserCode;

public static class Program
{
    public static void Main()
    {
        // create a string of `x` length filled with `c`
        int x = int.Parse(Console.ReadLine());
        char c = Console.ReadKey().KeyChar;

        // Oops! Accidental closure creation; Warning generated
        string s = string.Create(x, c, (state, span) => span.Fill(c));
        Console.WriteLine(s);
    }
}

Alternative Designs

Alternatively, a generic analyzer that detects methods with a "state" parameter (and delegates taking said state type) could be implemented, but that could have false positives and negatives.

Risks

If applied to the BCL, users passing closures could receive warnings they were not getting before.

@colejohnson66 colejohnson66 added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jan 18, 2024
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jan 18, 2024
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jan 18, 2024
@stephentoub
Copy link
Member

Example usage in the BCL

I'm not understanding in what situation we'd ever apply it. It's up to the caller how they want to invoke the method; from the method's perspective, it's not a problem if they want to allocate a new delegate, if they want to manually allocate an object the delegate points to, if they want to let the compiler do it for them with a closure, etc.

@colejohnson66
Copy link
Author

The idea is that some methods take a state parameter so as to avoid creating closures if possible. If the method can annotate the parameter as "avoid closures", then the author would be warned that a closure is being created. A bit similar to the "capture of variable modified in outer scope" warnings that already exist.

@ghost
Copy link

ghost commented Jan 19, 2024

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

Many times, it is desirable to want a static delegate, not a closure to be passed in. Currently, there is no way to warn against this. Sure, you can put static before the lambda definition to prevent a closure, but that is up to the caller to do so, not the callee.

For example, when writing a source generator, especially an incremental one, many of the delegate parameters should be static, but there is no warning if you accidentally create a closure.

Some analyzers do exist to warn about any closure allocation, such as the heap allocation viewer plugin for JetBrains Rider, but those are indiscriminate in their warnings - any closure is flagged. While useful in many cases, that is not always desired.

API Proposal

As far as the API is concerned, a single attribute would be added to the BCL:

namespace System;

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class DisallowClosuresAttribute : Attribute;

The logic would be implemented by the analyzer.

API Usage

Example usage in the BCL:

namespace System;

public class String
{
    public static string Create<TState>(
        int length,
        TState state,
        [DisallowClosures] SpanAction<char, TState> action); // note the new attribute
}

User experience:

namespace UserCode;

public static class Program
{
    public static void Main()
    {
        // create a string of `x` length filled with `c`
        int x = int.Parse(Console.ReadLine());
        char c = Console.ReadKey().KeyChar;

        // Oops! Accidental closure creation; Warning generated
        string s = string.Create(x, c, (state, span) => span.Fill(c));
        Console.WriteLine(s);
    }
}

Alternative Designs

Alternatively, a generic analyzer that detects methods with a "state" parameter (and delegates taking said state type) could be implemented, but that could have false positives and negatives.

Risks

If applied to the BCL, users passing closures could receive warnings they were not getting before.

Author: colejohnson66
Assignees: -
Labels:

api-suggestion, area-System.Runtime, untriaged, needs-area-label

Milestone: -

@vcsjones vcsjones removed the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jan 19, 2024
@tannergooding tannergooding added needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration and removed untriaged New issue has not been triaged by the area owner labels Jun 24, 2024
@stephentoub stephentoub added this to the Future milestone Jul 19, 2024
@akarpov89
Copy link
Contributor

akarpov89 commented Jan 21, 2025

@colejohnson66 FYI there's such attribute in the JetBrains.Annotations package, it's called RequireStaticDelegateAttribute, needless to say that ReSharper and Rider spot violations of this annotation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Projects
None yet
Development

No branches or pull requests

6 participants