Browse Source

Conditionally-included polyfills for language feature support in netstandard2.0

Brandon Thetford 1 year ago
parent
commit
86e8f6a1ec

+ 33 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/CompilerFeatureRequiredAttribute.cs

@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// ReSharper disable once CheckNamespace
+namespace System.Runtime.CompilerServices;
+
+/// <summary>
+///     Indicates that compiler support for a particular feature is required for the location where this attribute is
+///     applied.
+/// </summary>
+[AttributeUsage (AttributeTargets.All, AllowMultiple = true, Inherited = false)]
+internal sealed class CompilerFeatureRequiredAttribute(string featureName) : Attribute
+{
+    /// <summary>
+    ///     The <see cref="FeatureName"/> used for the ref structs C# feature.
+    /// </summary>
+    public const string RefStructs = nameof (RefStructs);
+
+    /// <summary>
+    ///     The <see cref="FeatureName"/> used for the required members C# feature.
+    /// </summary>
+    public const string RequiredMembers = nameof (RequiredMembers);
+
+    /// <summary>
+    ///     The name of the compiler feature.
+    /// </summary>
+    public string FeatureName { get; } = featureName;
+    /// <summary>
+    ///     If true, the compiler can choose to allow access to the location where this attribute is applied if it does not
+    ///     understand <see cref="FeatureName"/>.
+    /// </summary>
+    public bool IsOptional { get; init; }
+}

+ 11 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IEqualityOperators.cs

@@ -0,0 +1,11 @@
+// ReSharper disable once CheckNamespace
+namespace System.Numerics;
+/// <summary>
+/// Included for compatibility with .net7+, but has no members.
+/// Thus it cannot be explicitly used in generator code.
+/// Use it for static analysis only.
+/// </summary>
+/// <typeparam name="T">The left operand type.</typeparam>
+/// <typeparam name="T1">The right operand type.</typeparam>
+/// <typeparam name="T2">The return type.</typeparam>
+internal interface IEqualityOperators<T, T1, T2>;

+ 6 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IntrinsicAttribute.cs

@@ -0,0 +1,6 @@
+namespace System.Runtime.CompilerServices;
+
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)]
+public sealed class IntrinsicAttribute : Attribute
+{
+}

+ 18 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IsExternalInit.cs

@@ -0,0 +1,18 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+// ReSharper disable CheckNamespace
+namespace System.Runtime.CompilerServices;
+
+/// <summary>
+///     Reserved to be used by the compiler for tracking metadata.
+///     This class should not be used by developers in source code.
+/// </summary>
+/// <remarks>
+///     Copied from .net source code, for support of init property accessors in netstandard2.0.
+/// </remarks>
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[EditorBrowsable (EditorBrowsableState.Never)]
+public static class IsExternalInit;

+ 208 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NullableAttributes.cs

@@ -0,0 +1,208 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// This file is further modified from the original, for this project,
+// to comply with project style.
+// No changes are made which affect compatibility with the same types from
+// APIs later than netstandard2.0, nor will this file be included in compilations
+// targeted at later APIs.
+//
+// Originally rom https://github.com/dotnet/runtime/blob/ef72b95937703e485fdbbb75f3251fedfd1a0ef9/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
+
+// ReSharper disable CheckNamespace
+
+// ReSharper disable UnusedAutoPropertyAccessor.Global
+// ReSharper disable UnusedType.Global
+
+namespace System.Diagnostics.CodeAnalysis;
+
+/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class AllowNullAttribute : Attribute;
+
+/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class DisallowNullAttribute : Attribute;
+
+/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MaybeNullAttribute : Attribute;
+
+/// <summary>
+///     Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input
+///     argument was not null when the call returns.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class NotNullAttribute : Attribute;
+
+/// <summary>
+///     Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding
+///     type disallows it.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MaybeNullWhenAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified return value condition.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter may be null.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public MaybeNullWhenAttribute (bool returnValue) { ReturnValue = returnValue; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the return value condition.</summary>
+    public bool ReturnValue { get; }
+}
+
+/// <summary>
+///     Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the
+///     corresponding type allows it.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class NotNullWhenAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified return value condition.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter will not be null.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public NotNullWhenAttribute (bool returnValue) { ReturnValue = returnValue; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the return value condition.</summary>
+    public bool ReturnValue { get; }
+}
+
+/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class NotNullIfNotNullAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the associated parameter name.</summary>
+    /// <param name="parameterName">
+    ///     The associated parameter name.  The output will be non-null if the argument to the parameter specified is non-null.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public NotNullIfNotNullAttribute (string parameterName) { ParameterName = parameterName; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the associated parameter name.</summary>
+    public string ParameterName { get; }
+}
+
+/// <summary>Applied to a method that will never return under any circumstance.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Method, Inherited = false)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class DoesNotReturnAttribute : Attribute;
+
+/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class DoesNotReturnIfAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified parameter value.</summary>
+    /// <param name="parameterValue">
+    ///     The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument
+    ///     to
+    ///     the associated parameter matches this value.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public DoesNotReturnIfAttribute (bool parameterValue) { ParameterValue = parameterValue; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the condition parameter value.</summary>
+    public bool ParameterValue { get; }
+}
+
+/// <summary>
+///     Specifies that the method or property will ensure that the listed field and property members have not-null
+///     values.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MemberNotNullAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with a field or property member.</summary>
+    /// <param name="member">
+    ///     The field or property member that is promised to be not-null.
+    /// </param>
+    public MemberNotNullAttribute (string member) { Members = [member]; }
+
+    /// <summary>Initializes the attribute with the list of field and property members.</summary>
+    /// <param name="members">
+    ///     The list of field and property members that are promised to be not-null.
+    /// </param>
+    public MemberNotNullAttribute (params string [] members) { Members = members; }
+
+    /// <summary>Gets field or property member names.</summary>
+    public string [] Members { get; }
+}
+
+/// <summary>
+///     Specifies that the method or property will ensure that the listed field and property members have not-null values
+///     when returning with the specified return value condition.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MemberNotNullWhenAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified return value condition and a field or property member.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter will not be null.
+    /// </param>
+    /// <param name="member">
+    ///     The field or property member that is promised to be not-null.
+    /// </param>
+    public MemberNotNullWhenAttribute (bool returnValue, string member)
+    {
+        ReturnValue = returnValue;
+        Members = [member];
+    }
+
+    /// <summary>Initializes the attribute with the specified return value condition and list of field and property members.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter will not be null.
+    /// </param>
+    /// <param name="members">
+    ///     The list of field and property members that are promised to be not-null.
+    /// </param>
+    public MemberNotNullWhenAttribute (bool returnValue, params string [] members)
+    {
+        ReturnValue = returnValue;
+        Members = members;
+    }
+
+    /// <summary>Gets field or property member names.</summary>
+    public string [] Members { get; }
+
+    /// <summary>Gets the return value condition.</summary>
+    public bool ReturnValue { get; }
+}

+ 43 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NumericExtensions.cs

@@ -0,0 +1,43 @@
+// ReSharper disable once CheckNamespace
+namespace Terminal.Gui.Analyzers.Internal.Compatibility;
+
+/// <summary>
+///     Extension methods for <see langword="int"/> and <see langword="uint"/> types.
+/// </summary>
+/// <remarks>
+///     This is mostly just for backward compatibility with netstandard2.0.
+/// </remarks>
+public static class NumericExtensions
+{
+    /// <summary>
+    ///     Gets the population count (number of bits set to 1) of this 32-bit value.
+    /// </summary>
+    /// <param name="value">The value to get the population count of.</param>
+    /// <remarks>
+    ///     The algorithm is the well-known SWAR (SIMD Within A Register) method for population count.<br/>
+    ///     Included for hardware- and runtime- agnostic support for the equivalent of the x86 popcnt instruction, since
+    ///     System.Numerics.Intrinsics isn't available in netstandard2.0.<br/>
+    ///     It performs the operation simultaneously on 4 bytes at a time, rather than the naive method of testing all 32 bits
+    ///     individually.<br/>
+    ///     Most compilers can recognize this and turn it into a single platform-specific instruction, when available.
+    /// </remarks>
+    /// <returns>
+    ///     An unsigned 32-bit integer value containing the population count of <paramref name="value"/>.
+    /// </returns>
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    public static uint GetPopCount (this uint value)
+    {
+        unchecked
+        {
+            value -= (value >> 1) & 0x55555555;
+            value = (value & 0x33333333) + ((value >> 2) & 0x33333333);
+            value = (value + (value >> 4)) & 0x0F0F0F0F;
+
+            return (value * 0x01010101) >> 24;
+        }
+    }
+
+    /// <inheritdoc cref="GetPopCount(uint)"/>
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    public static uint GetPopCount (this int value) { return GetPopCount (Unsafe.As<int, uint> (ref value)); }
+}

+ 12 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/RequiredMemberAttribute.cs

@@ -0,0 +1,12 @@
+// ReSharper disable CheckNamespace
+// ReSharper disable ConditionalAnnotation
+
+using JetBrains.Annotations;
+
+namespace System.Runtime.CompilerServices;
+
+/// <summary>Polyfill to enable netstandard2.0 assembly to use the required keyword.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Property)]
+[UsedImplicitly]
+public sealed class RequiredMemberAttribute : Attribute;

+ 12 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/SetsRequiredMembersAttribute.cs

@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// ReSharper disable once CheckNamespace
+namespace System.Diagnostics.CodeAnalysis;
+
+/// <summary>
+///     Specifies that this constructor sets all required members for the current type, and callers
+///     do not need to set any required members themselves.
+/// </summary>
+[AttributeUsage (AttributeTargets.Constructor)]
+public sealed class SetsRequiredMembersAttribute : Attribute;

+ 14 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/SkipLocalsInitAttribute.cs

@@ -0,0 +1,14 @@
+namespace System.Runtime.CompilerServices;
+
+[AttributeUsage (
+                    AttributeTargets.Class
+                    | AttributeTargets.Constructor
+                    | AttributeTargets.Event
+                    | AttributeTargets.Interface
+                    | AttributeTargets.Method
+                    | AttributeTargets.Module
+                    | AttributeTargets.Property
+                    | AttributeTargets.Struct,
+                    Inherited = false)]
+
+internal sealed class SkipLocalsInitAttribute : Attribute;