Skip to content

Pass constants to parameters marked as [ConstantExpected] #33771

Closed
dotnet/roslyn-analyzers
#5766

Description

@terrajobst

Consider adding an [ConstantExpected] attribute that could be put on parameters to indicate that arguments should be constants rather than variables. See #30740 (comment) for details.

The attribute proposal

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public sealed class ConstantExpectedAttribute : Attribute
    {
        public object? Min { get; set; }
        public object? Max { get; set; }
    }
}

Usage and Diagnostic examples

public static class C
{
    public static void MyBool1([ConstantExpected] bool b) { }
    public static void MyLong1([ConstantExpected] long b) { }
    public static void MyLong2([ConstantExpected(Min = -5, Max = 10)] long b) { }
    public static void MyFloat1([ConstantExpected] float b) { }
    public static void MyFloat2([ConstantExpected(Min = -5.3f, Max = 10.1f)] float b) { }

    // Might want to warn for the negative values and out of range values
    public static void MyInvalidUshort([ConstantExpected(Min = -5, Max = -1)] ushort b) { }
    public static void MyInvalidRange([ConstantExpected(Min = 5, Max = -5)] int b) { }
    // flag any ref type usage as not applicable
    public static void MyString([ConstantExpected] string b) { }

    // Diagnostics examples
    public static void Test(long b, ushort u)
    {
        // OK
        const long a = 10;
        MyLong1(a);
        MyLong2(a);
        MyLong1(1L);
        MyLong2(2L);
        MyInvalidUshort(1);
        const ushort us = 0;
        MyInvalidUshort(us); // Flag

        MyLong1(b); // Flag
        MyLong2(b); // Flag

                
        MyLong2(20); // Flag, out of range
        MyInvalidUshort(u); // Flag
        MyInvalidUshort(10); // Flag, out of the range
    }
}

Category: Reliability
Severity: Info

Activity

  1. added this to the 5.0 milestone on Mar 20, 2020
  2. jeffhandley commented on Mar 21, 2020

    @jeffhandley
    Member

    Estimates:

    • Analyzer: Medium
    • Fixer: Not Applicable
  3. removed
    untriagedNew issue has not been triaged by the area owner
    on Mar 21, 2020
  4. jeffhandley commented on Mar 23, 2020

    @jeffhandley
    Member

    This issue should capture:

    1. The attribute itself (getting that through the API review)
    2. Creation of the analyzer
    3. Identification of some instances where the attribute should be applied

    The work to apply the attribute (especially through intrinsics) could be a separate issue/PR.

  5. added
    api-needs-workAPI needs work before it is approved, it is NOT ready for implementation
    and removed
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on Mar 23, 2020
  6. jeffhandley commented on May 26, 2020

    @jeffhandley
    Member

    @tannergooding Do you think this is a must-have attribute & analyzer for .NET 5 to round out the intrinsics work we've done, or do you think this could be added in .NET 6?

  7. tannergooding commented on May 26, 2020

    @tannergooding
    Member

    It is not. The hardware intrinsics don't even require constant inputs, they just benefit from them and you will get very poor codegen if you don't.
    We shipped .NET Core 3.0 without such an attribute and I'm sure we'll be fine if we do the same for .NET 5.

  8. 44 remaining items

  9. wzchua commented on Dec 11, 2021

    @wzchua
    Contributor

    Do we allow composition of different types? Like what should the analyzer do with something like below:

    public static void TestMethod([ConstantExpected(Min=0, Max=5)] uint constant)
    {
        TestMethod2(constant);
    }
    public static void TestMethod2([ConstantExpected(Min=0, Max=10)] int val)
    {
    }
  10. wzchua commented on Dec 13, 2021

    @wzchua
    Contributor

    Also, does casting influence the analyzer?

    TestMethod((uint)100);
  11. buyaa-n commented on Dec 13, 2021

    @buyaa-n
    Contributor

    Do we allow composition of different types? Like what should the analyzer do with something like below:

    Not sure I follow the question, though I believe the analyzer should warn on TestMethod2(constant); invocation because it is not passing a constant

    public static void TestMethod([ConstantExpected(Min=0, Max=5)] uint constant)
    {
        TestMethod2(constant); // should warn here as constant expected for 'val' parameter
    }
    public static void TestMethod2([ConstantExpected(Min=0, Max=10)] int val)
    {
    }

    Also, does casting influence the analyzer?

    TestMethod((uint)100);

    Not sure what issue you are having here, but TestMethod((uint)100); and TestMethod(100); in your example should behave the same

  12. wzchua commented on Dec 13, 2021

    @wzchua
    Contributor

    It seems we want to be able to compose the ConstantExpectedAttribute using another ConstantExpectedAttribute. I'm clarifying what should be done if the types don't match.

    I guess I'm asking does casting make it not a constant?
    It might be more relevant for the composition case.
    I guess it is not an issue for the literal scenario.

  13. buyaa-n commented on Dec 13, 2021

    @buyaa-n
    Contributor

    Good question, tagging @tannergooding @GrabYourPitchforks @bartonjs for answer

  14. wzchua commented on Dec 17, 2021

    @wzchua
    Contributor

    Given,

    using System;
    using System.Diagnostics.CodeAnalysis;
    #nullable enable
    
    public class Test
    {
        public interface ITest<T>
        {
            T Add(T operand1, [ConstantExpected] T operand2);
        }
        public abstract class AbstractTest<T>
        {
            public abstract T Add2(T operand1, [ConstantExpected] T operand2);
        }
        public class GenericInt : AbstractTest<int>, ITest<int>
        {
            public int Add(int operand1, int operand2)
            {
                return 0;
            }
            public override int Add2(int operand1, int operand2)
            {
                return 0;
            }
        }
    }

    I assume the analyzer should warn for not having [ConstantExpected] in the more concrete type parameter?

  15. tannergooding commented on Jan 7, 2022

    @tannergooding
    Member

    I assume the analyzer should warn for not having [ConstantExpected] in the more concrete type parameter?

    I'd expect so, yes. There is some "wonkiness" here though, in that [ConstantExpected] is "impossible" to fulfill for non-primitive types. So if T were a Guid, for example, we'd need to decide what happens.

  16. wzchua commented on Jan 9, 2022

    @wzchua
    Contributor

    Ok, i have written the tests for many of the cases.
    For cases where the attribute is applied to T, setting Min Max is an error, and warning at callsite only appears if concrete type for T can be constant

  17. deeprobin commented on Feb 24, 2022

    @deeprobin
    Contributor
  18. bartonjs commented on Feb 24, 2022

    @bartonjs
    Member

    @deeprobin The general stance is "most analyzers should Just Work for both C# and VB, so you should add VB tests and enjoy that it Just Worked... but if your analyzer requires special VB code it's probably not worth it"

  19. modified the milestones: Future, 8.0.0 on Nov 9, 2022
  20. Repository owner moved this from vNext to Done in Triage POD for Reflection, META, etc.on Nov 16, 2022
  21. ghost locked as resolved and limited conversation to collaborators on Dec 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions