diff --git a/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs b/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs index 6bcf8ea..cbd1b69 100644 --- a/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs +++ b/src/NetEscapades.EnumGenerators/SourceGenerationHelper.cs @@ -948,7 +948,7 @@ static file class EnumInterceptors sb.AppendLine( $$""" public static string {{toIntercept.ExtensionTypeName}}ToString(this global::System.Enum value) - => global::{{toIntercept.EnumNamespace}}.{{toIntercept.ExtensionTypeName}}.ToStringFast((global::{{toIntercept.FullyQualifiedName}})value); + => global::{{toIntercept.EnumNamespace}}{{(string.IsNullOrEmpty(toIntercept.EnumNamespace) ? "" : ".")}}{{toIntercept.ExtensionTypeName}}.ToStringFast((global::{{toIntercept.FullyQualifiedName}})value); """); } @@ -968,7 +968,7 @@ static file class EnumInterceptors sb.AppendLine( $$""" public static bool {{toIntercept.ExtensionTypeName}}HasFlag(this global::System.Enum value, global::System.Enum flag) - => global::{{toIntercept.EnumNamespace}}.{{toIntercept.ExtensionTypeName}}.HasFlagFast((global::{{toIntercept.FullyQualifiedName}})value, (global::{{toIntercept.FullyQualifiedName}})flag); + => global::{{toIntercept.EnumNamespace}}{{(string.IsNullOrEmpty(toIntercept.EnumNamespace) ? "" : ".")}}{{toIntercept.ExtensionTypeName}}.HasFlagFast((global::{{toIntercept.FullyQualifiedName}})value, (global::{{toIntercept.FullyQualifiedName}})flag); """); } diff --git a/tests/NetEscapades.EnumGenerators.Tests/InterceptorTests.cs b/tests/NetEscapades.EnumGenerators.Tests/InterceptorTests.cs index 8a978e3..9117ac4 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/InterceptorTests.cs +++ b/tests/NetEscapades.EnumGenerators.Tests/InterceptorTests.cs @@ -105,6 +105,91 @@ public void MyTest() return Verifier.Verify(output).ScrubExpectedChanges().UseDirectory("Snapshots"); } + [Fact] + public Task CanInterceptEnumInGlobalNamespace() + { + const string input = + """ + using NetEscapades.EnumGenerators; + using System; + + [EnumExtensions] + [Flags] + internal enum MyEnum + { + First = 1, + Second = 2, + Third = 4, + } + + internal class InnerClass + { + public MyEnum _field = default; + public MyEnum Property {get;set;} = default; + public void MyTest() + { + var myValue = MyEnum.Second; + var var1 = myValue.HasFlag(MyEnum.First); + var var2 = MyEnum.Second.HasFlag(myValue); + var var3 = Property.HasFlag(MyEnum.First); + var var4 = _field.HasFlag(MyEnum.First); + var var5 = myValue.ToString(); + } + } + """; + var (diagnostics, output) = + TestHelpers.GetGeneratedTrees(new(_analyzerOpts, _features, input)); + + Assert.Empty(diagnostics); + return Verifier.Verify(output).ScrubExpectedChanges().UseDirectory("Snapshots"); + } + + [Fact] + public Task CanInterceptEnumInDifferentNamespace() + { + const string input = + """ + using NetEscapades.EnumGenerators; + using System; + + namespace Foo + { + [EnumExtensions(ExtensionClassNamespace = "Bar", ExtensionClassName = "SomethingElse")] + [Flags] + internal enum MyEnum + { + First = 1, + Second = 2, + Third = 4, + } + } + + namespace Baz + { + using Foo; + internal class InnerClass + { + public MyEnum _field = default; + public MyEnum Property {get;set;} = default; + public void MyTest() + { + var myValue = MyEnum.Second; + var var1 = myValue.HasFlag(MyEnum.First); + var var2 = MyEnum.Second.HasFlag(myValue); + var var3 = Property.HasFlag(MyEnum.First); + var var4 = _field.HasFlag(MyEnum.First); + var var5 = myValue.ToString(); + } + } + } + """; + var (diagnostics, output) = + TestHelpers.GetGeneratedTrees(new(_analyzerOpts, _features, input)); + + Assert.Empty(diagnostics); + return Verifier.Verify(output).ScrubExpectedChanges().UseDirectory("Snapshots"); + } + [Fact] public Task CanInterceptMultipleEnumsToString() { diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInDifferentNamespace.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInDifferentNamespace.verified.txt new file mode 100644 index 0000000..8046092 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInDifferentNamespace.verified.txt @@ -0,0 +1,698 @@ +[ +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +#if NETESCAPADES_ENUMGENERATORS_EMBED_ATTRIBUTES +namespace NetEscapades.EnumGenerators +{ + /// + /// Add to enums to indicate that extension methods should be generated for the type + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Enum)] + [global::System.Diagnostics.Conditional("NETESCAPADES_ENUMGENERATORS_USAGES")] +#if NET5_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Generated by the NetEscapades.EnumGenerators source generator.")] +#else + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +#endif + public class EnumExtensionsAttribute : global::System.Attribute + { + /// + /// The namespace to generate the extension class. + /// If not provided, the namespace of the enum will be used + /// + public string? ExtensionClassNamespace { get; set; } + + /// + /// The name to use for the extension class. + /// If not provided, the enum name with "Extensions" will be used. + /// For example for an Enum called StatusCodes, the default name + /// will be StatusCodesExtensions + /// + public string? ExtensionClassName { get; set; } + + /// + /// By default, when interceptors are enabled for a project, any + /// interceptable usages of the enum will be replaced by usages of + /// the extension method in this project. To disable interception of + /// the enum in this project, set to false. + /// + public bool IsInterceptable { get; set; } = true; + } + + + /// + /// Add to enums to indicate that extension methods should be generated for the type + /// + [System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)] + [System.Diagnostics.Conditional("NETESCAPADES_ENUMGENERATORS_USAGES")] +#if NET5_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Generated by the NetEscapades.EnumGenerators source generator.")] +#else + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +#endif + public class EnumExtensionsAttribute : System.Attribute + where T: System.Enum + { + /// + /// The namespace to generate the extension class. + /// If not provided, the namespace of the enum will be used + /// + public string? ExtensionClassNamespace { get; set; } + + /// + /// The name to use for the extension class. + /// If not provided, the enum name with ""Extensions"" will be used. + /// For example for an Enum called StatusCodes, the default name + /// will be StatusCodesExtensions + /// + public string? ExtensionClassName { get; set; } + + /// + /// By default, when interceptors are enabled for a project, any + /// interceptable usages of the enum will be replaced by usages of + /// the extension method in this project. To disable interception of + /// the enum in this project, set to false. + /// + public bool IsInterceptable { get; set; } = true; + } + + /// + /// Add to an assembly to indicate that usages of the enum should + /// be automatically intercepted to use the extension methods + /// generated by in this project. + /// Note that the extension methods must be accessible from this project, + /// otherwise you will receive compilation errors + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Assembly, AllowMultiple = true)] + [global::System.Diagnostics.Conditional("NETESCAPADES_ENUMGENERATORS_USAGES")] +#if NET5_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Generated by the NetEscapades.EnumGenerators source generator.")] +#else + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +#endif + public class InterceptableAttribute : global::System.Attribute + where T: global::System.Enum + { + /// + /// The namespace generated for the extension class. + /// If not provided, the namespace of the enum will be assumed. + /// + public string? ExtensionClassNamespace { get; set; } + + /// + /// The name used for the extension class. + /// If not provided, the enum name with ""Extensions"" will be assumed. + /// For example for an Enum called StatusCodes, the assumed name + /// will be StatusCodesExtensions. + /// + public string? ExtensionClassName { get; set; } + } +} +#endif + + +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable +#if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_1 && !NETCOREAPP1_0 +using System; +#endif + +namespace Bar +{ +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums + /// + /// Extension methods for + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("NetEscapades.EnumGenerators", "FIXED_VERSION")] + internal static partial class SomethingElse + { + /// + /// The number of members in the enum. + /// This is a non-distinct count of defined names. + /// + public const int Length = 3; + + /// + /// Returns the string representation of the value. + /// If the attribute is decorated with a [Display] attribute, then + /// uses the provided value. Otherwise uses the name of the member, equivalent to + /// calling ToString() on . + /// + /// The value to retrieve the string value for + /// The string representation of the value + public static string ToStringFast(this global::Foo.MyEnum value) + => value switch + { + global::Foo.MyEnum.First => nameof(global::Foo.MyEnum.First), + global::Foo.MyEnum.Second => nameof(global::Foo.MyEnum.Second), + global::Foo.MyEnum.Third => nameof(global::Foo.MyEnum.Third), + _ => value.ToString(), + }; + + /// + /// Determines whether one or more bit fields are set in the current instance. + /// Equivalent to calling on . + /// + /// The value of the instance to investigate + /// The flag to check for + /// true if the fields set in the flag are also set in the current instance; otherwise false. + /// If the underlying value of is zero, the method returns true. + /// This is consistent with the behaviour of + public static bool HasFlagFast(this global::Foo.MyEnum value, global::Foo.MyEnum flag) + => flag == 0 ? true : (value & flag) == flag; + + /// + /// Returns a boolean telling whether the given enum value exists in the enumeration. + /// + /// The value to check if it's defined + /// true if the value exists in the enumeration, false otherwise + public static bool IsDefined(global::Foo.MyEnum value) + => value switch + { + global::Foo.MyEnum.First => true, + global::Foo.MyEnum.Second => true, + global::Foo.MyEnum.Third => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration. + /// + /// The name to check if it's defined + /// true if a member with the name exists in the enumeration, false otherwise + public static bool IsDefined(string name) => IsDefined(name, allowMatchingMetadataAttribute: false); + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or if a member decorated with a [Display] attribute + /// with the required name exists. + /// + /// The name to check if it's defined + /// If true, considers the value of metadata attributes,otherwise ignores them + /// true if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, false otherwise + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch + { + nameof(global::Foo.MyEnum.First) => true, + nameof(global::Foo.MyEnum.Second) => true, + nameof(global::Foo.MyEnum.Third) => true, + _ => false, + }; + } + +#if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_1 && !NETCOREAPP1_0 + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration + /// + /// The name to check if it's defined + /// true if a member with the name exists in the enumeration, false otherwise + public static bool IsDefined(in ReadOnlySpan name) => IsDefined(name, allowMatchingMetadataAttribute: false); + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or optionally if a member decorated with a [Display] attribute + /// with the required name exists. + /// Slower then the overload, but doesn't allocate memory./> + /// + /// The name to check if it's defined + /// If true, considers the value of metadata attributes,otherwise ignores them + /// true if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, false otherwise + public static bool IsDefined(in ReadOnlySpan name, bool allowMatchingMetadataAttribute) + { + return name switch + { + ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.First).AsSpan(), global::System.StringComparison.Ordinal) => true, + ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.Second).AsSpan(), global::System.StringComparison.Ordinal) => true, + ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.Third).AsSpan(), global::System.StringComparison.Ordinal) => true, + _ => false, + }; + } +#endif + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Foo.MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name) + => TryParse(name, out var value, false, false) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Foo.MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase) + => TryParse(name, out var value, ignoreCase, false) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Foo.MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse(name, out var value, ignoreCase, allowMatchingMetadataAttribute) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::Foo.MyEnum ThrowValueNotFound(string? name) + => throw new System.ArgumentException($"Requested value '{name}' was not found."); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Foo.MyEnum value) + => TryParse(name, out value, false, false); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Foo.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Foo.MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => ignoreCase + ? TryParseIgnoreCase(name, out value, allowMatchingMetadataAttribute) + : TryParseWithCase(name, out value, allowMatchingMetadataAttribute); + + private static bool TryParseIgnoreCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Foo.MyEnum value, + bool allowMatchingMetadataAttribute) + { + + switch (name) + { + case string s when s.Equals(nameof(global::Foo.MyEnum.First), global::System.StringComparison.OrdinalIgnoreCase): + value = global::Foo.MyEnum.First; + return true; + case string s when s.Equals(nameof(global::Foo.MyEnum.Second), global::System.StringComparison.OrdinalIgnoreCase): + value = global::Foo.MyEnum.Second; + return true; + case string s when s.Equals(nameof(global::Foo.MyEnum.Third), global::System.StringComparison.OrdinalIgnoreCase): + value = global::Foo.MyEnum.Third; + return true; + case string s when int.TryParse(name, out var val): + value = (global::Foo.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + + private static bool TryParseWithCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::Foo.MyEnum value, + bool allowMatchingMetadataAttribute) + { + + switch (name) + { + case nameof(global::Foo.MyEnum.First): + value = global::Foo.MyEnum.First; + return true; + case nameof(global::Foo.MyEnum.Second): + value = global::Foo.MyEnum.Second; + return true; + case nameof(global::Foo.MyEnum.Third): + value = global::Foo.MyEnum.Third; + return true; + case string s when int.TryParse(name, out var val): + value = (global::Foo.MyEnum)val; + return true; + default: + value = default; + return false; + } + } + +#if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_1 && !NETCOREAPP1_0 + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::Foo.MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name) + => TryParse(name, out var value, false, false) ? value : ThrowValueNotFound(name.ToString()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::Foo.MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + bool ignoreCase) + => TryParse(name, out var value, ignoreCase, false) ? value : ThrowValueNotFound(name.ToString()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::Foo.MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse(name, out var value, ignoreCase, allowMatchingMetadataAttribute) ? value : ThrowValueNotFound(name.ToString()); + + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::Foo.MyEnum value) + => TryParse(name, out value, false, false); + + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::Foo.MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); + + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::Foo.MyEnum result, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => ignoreCase + ? TryParseIgnoreCase(in name, out result, allowMatchingMetadataAttribute) + : TryParseWithCase(in name, out result, allowMatchingMetadataAttribute); + + private static bool TryParseIgnoreCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::Foo.MyEnum result, + bool allowMatchingMetadataAttribute) + { + switch (name) + { + case ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.First).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase): + result = global::Foo.MyEnum.First; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.Second).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase): + result = global::Foo.MyEnum.Second; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.Third).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase): + result = global::Foo.MyEnum.Third; + return true; + case ReadOnlySpan current when int.TryParse(name, out var numericResult): + result = (global::Foo.MyEnum)numericResult; + return true; + default: + result = default; + return false; + } + } + + private static bool TryParseWithCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::Foo.MyEnum result, + bool allowMatchingMetadataAttribute) + { + switch (name) + { + case ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.First).AsSpan(), global::System.StringComparison.Ordinal): + result = global::Foo.MyEnum.First; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.Second).AsSpan(), global::System.StringComparison.Ordinal): + result = global::Foo.MyEnum.Second; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::Foo.MyEnum.Third).AsSpan(), global::System.StringComparison.Ordinal): + result = global::Foo.MyEnum.Third; + return true; + case ReadOnlySpan current when int.TryParse(name, out var numericResult): + result = (global::Foo.MyEnum)numericResult; + return true; + default: + result = default; + return false; + } + } +#endif + + /// + /// Retrieves an array of the values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the values defined in + public static global::Foo.MyEnum[] GetValues() + { + return new[] + { + global::Foo.MyEnum.First, + global::Foo.MyEnum.Second, + global::Foo.MyEnum.Third, + }; + } + + /// + /// Retrieves an array of the names of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the names of the members defined in + public static string[] GetNames() + { + return new[] + { + nameof(global::Foo.MyEnum.First), + nameof(global::Foo.MyEnum.Second), + nameof(global::Foo.MyEnum.Third), + }; + } + } +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums +} + +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable +namespace System.Runtime.CompilerServices +{ + // this type is needed by the compiler to implement interceptors - it doesn't need to + // come from the runtime itself, though + + [global::System.Diagnostics.Conditional("DEBUG")] // not needed post-build, so: evaporate + [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] + sealed file class InterceptsLocationAttribute : global::System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + _ = version; + _ = data; + } + } +} + +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums +namespace NetEscapades.EnumGenerators +{ + static file class EnumInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(30,32) + public static string SomethingElseToString(this global::System.Enum value) + => global::Bar.SomethingElse.ToStringFast((global::Foo.MyEnum)value); + + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(26,32) + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(27,38) + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(28,33) + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(29,31) + public static bool SomethingElseHasFlag(this global::System.Enum value, global::System.Enum flag) + => global::Bar.SomethingElse.HasFlagFast((global::Foo.MyEnum)value, (global::Foo.MyEnum)flag); + + } +} +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums + +] \ No newline at end of file diff --git a/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInGlobalNamespace.verified.txt b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInGlobalNamespace.verified.txt new file mode 100644 index 0000000..af41ab1 --- /dev/null +++ b/tests/NetEscapades.EnumGenerators.Tests/Snapshots/InterceptorTests.CanInterceptEnumInGlobalNamespace.verified.txt @@ -0,0 +1,695 @@ +[ +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable + +#if NETESCAPADES_ENUMGENERATORS_EMBED_ATTRIBUTES +namespace NetEscapades.EnumGenerators +{ + /// + /// Add to enums to indicate that extension methods should be generated for the type + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Enum)] + [global::System.Diagnostics.Conditional("NETESCAPADES_ENUMGENERATORS_USAGES")] +#if NET5_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Generated by the NetEscapades.EnumGenerators source generator.")] +#else + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +#endif + public class EnumExtensionsAttribute : global::System.Attribute + { + /// + /// The namespace to generate the extension class. + /// If not provided, the namespace of the enum will be used + /// + public string? ExtensionClassNamespace { get; set; } + + /// + /// The name to use for the extension class. + /// If not provided, the enum name with "Extensions" will be used. + /// For example for an Enum called StatusCodes, the default name + /// will be StatusCodesExtensions + /// + public string? ExtensionClassName { get; set; } + + /// + /// By default, when interceptors are enabled for a project, any + /// interceptable usages of the enum will be replaced by usages of + /// the extension method in this project. To disable interception of + /// the enum in this project, set to false. + /// + public bool IsInterceptable { get; set; } = true; + } + + + /// + /// Add to enums to indicate that extension methods should be generated for the type + /// + [System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)] + [System.Diagnostics.Conditional("NETESCAPADES_ENUMGENERATORS_USAGES")] +#if NET5_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Generated by the NetEscapades.EnumGenerators source generator.")] +#else + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +#endif + public class EnumExtensionsAttribute : System.Attribute + where T: System.Enum + { + /// + /// The namespace to generate the extension class. + /// If not provided, the namespace of the enum will be used + /// + public string? ExtensionClassNamespace { get; set; } + + /// + /// The name to use for the extension class. + /// If not provided, the enum name with ""Extensions"" will be used. + /// For example for an Enum called StatusCodes, the default name + /// will be StatusCodesExtensions + /// + public string? ExtensionClassName { get; set; } + + /// + /// By default, when interceptors are enabled for a project, any + /// interceptable usages of the enum will be replaced by usages of + /// the extension method in this project. To disable interception of + /// the enum in this project, set to false. + /// + public bool IsInterceptable { get; set; } = true; + } + + /// + /// Add to an assembly to indicate that usages of the enum should + /// be automatically intercepted to use the extension methods + /// generated by in this project. + /// Note that the extension methods must be accessible from this project, + /// otherwise you will receive compilation errors + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Assembly, AllowMultiple = true)] + [global::System.Diagnostics.Conditional("NETESCAPADES_ENUMGENERATORS_USAGES")] +#if NET5_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Generated by the NetEscapades.EnumGenerators source generator.")] +#else + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +#endif + public class InterceptableAttribute : global::System.Attribute + where T: global::System.Enum + { + /// + /// The namespace generated for the extension class. + /// If not provided, the namespace of the enum will be assumed. + /// + public string? ExtensionClassNamespace { get; set; } + + /// + /// The name used for the extension class. + /// If not provided, the enum name with ""Extensions"" will be assumed. + /// For example for an Enum called StatusCodes, the assumed name + /// will be StatusCodesExtensions. + /// + public string? ExtensionClassName { get; set; } + } +} +#endif + + +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable +#if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_1 && !NETCOREAPP1_0 +using System; +#endif + +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums + /// + /// Extension methods for + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("NetEscapades.EnumGenerators", "FIXED_VERSION")] + internal static partial class MyEnumExtensions + { + /// + /// The number of members in the enum. + /// This is a non-distinct count of defined names. + /// + public const int Length = 3; + + /// + /// Returns the string representation of the value. + /// If the attribute is decorated with a [Display] attribute, then + /// uses the provided value. Otherwise uses the name of the member, equivalent to + /// calling ToString() on . + /// + /// The value to retrieve the string value for + /// The string representation of the value + public static string ToStringFast(this global::MyEnum value) + => value switch + { + global::MyEnum.First => nameof(global::MyEnum.First), + global::MyEnum.Second => nameof(global::MyEnum.Second), + global::MyEnum.Third => nameof(global::MyEnum.Third), + _ => value.ToString(), + }; + + /// + /// Determines whether one or more bit fields are set in the current instance. + /// Equivalent to calling on . + /// + /// The value of the instance to investigate + /// The flag to check for + /// true if the fields set in the flag are also set in the current instance; otherwise false. + /// If the underlying value of is zero, the method returns true. + /// This is consistent with the behaviour of + public static bool HasFlagFast(this global::MyEnum value, global::MyEnum flag) + => flag == 0 ? true : (value & flag) == flag; + + /// + /// Returns a boolean telling whether the given enum value exists in the enumeration. + /// + /// The value to check if it's defined + /// true if the value exists in the enumeration, false otherwise + public static bool IsDefined(global::MyEnum value) + => value switch + { + global::MyEnum.First => true, + global::MyEnum.Second => true, + global::MyEnum.Third => true, + _ => false, + }; + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration. + /// + /// The name to check if it's defined + /// true if a member with the name exists in the enumeration, false otherwise + public static bool IsDefined(string name) => IsDefined(name, allowMatchingMetadataAttribute: false); + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or if a member decorated with a [Display] attribute + /// with the required name exists. + /// + /// The name to check if it's defined + /// If true, considers the value of metadata attributes,otherwise ignores them + /// true if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, false otherwise + public static bool IsDefined(string name, bool allowMatchingMetadataAttribute) + { + return name switch + { + nameof(global::MyEnum.First) => true, + nameof(global::MyEnum.Second) => true, + nameof(global::MyEnum.Third) => true, + _ => false, + }; + } + +#if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_1 && !NETCOREAPP1_0 + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration + /// + /// The name to check if it's defined + /// true if a member with the name exists in the enumeration, false otherwise + public static bool IsDefined(in ReadOnlySpan name) => IsDefined(name, allowMatchingMetadataAttribute: false); + + /// + /// Returns a boolean telling whether an enum with the given name exists in the enumeration, + /// or optionally if a member decorated with a [Display] attribute + /// with the required name exists. + /// Slower then the overload, but doesn't allocate memory./> + /// + /// The name to check if it's defined + /// If true, considers the value of metadata attributes,otherwise ignores them + /// true if a member with the name exists in the enumeration, or a member is decorated + /// with a [Display] attribute with the name, false otherwise + public static bool IsDefined(in ReadOnlySpan name, bool allowMatchingMetadataAttribute) + { + return name switch + { + ReadOnlySpan current when current.Equals(nameof(global::MyEnum.First).AsSpan(), global::System.StringComparison.Ordinal) => true, + ReadOnlySpan current when current.Equals(nameof(global::MyEnum.Second).AsSpan(), global::System.StringComparison.Ordinal) => true, + ReadOnlySpan current when current.Equals(nameof(global::MyEnum.Third).AsSpan(), global::System.StringComparison.Ordinal) => true, + _ => false, + }; + } +#endif + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name) + => TryParse(name, out var value, false, false) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase) + => TryParse(name, out var value, ignoreCase, false) ? value : ThrowValueNotFound(name); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse(name, out var value, ignoreCase, allowMatchingMetadataAttribute) ? value : ThrowValueNotFound(name); + +#if NETCOREAPP3_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.DoesNotReturn] +#endif + private static global::MyEnum ThrowValueNotFound(string? name) + => throw new System.ArgumentException($"Requested value '{name}' was not found."); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::MyEnum value) + => TryParse(name, out value, false, false); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The string representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::MyEnum value, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => ignoreCase + ? TryParseIgnoreCase(name, out value, allowMatchingMetadataAttribute) + : TryParseWithCase(name, out value, allowMatchingMetadataAttribute); + + private static bool TryParseIgnoreCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::MyEnum value, + bool allowMatchingMetadataAttribute) + { + + switch (name) + { + case string s when s.Equals(nameof(global::MyEnum.First), global::System.StringComparison.OrdinalIgnoreCase): + value = global::MyEnum.First; + return true; + case string s when s.Equals(nameof(global::MyEnum.Second), global::System.StringComparison.OrdinalIgnoreCase): + value = global::MyEnum.Second; + return true; + case string s when s.Equals(nameof(global::MyEnum.Third), global::System.StringComparison.OrdinalIgnoreCase): + value = global::MyEnum.Third; + return true; + case string s when int.TryParse(name, out var val): + value = (global::MyEnum)val; + return true; + default: + value = default; + return false; + } + } + + private static bool TryParseWithCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + string? name, + out global::MyEnum value, + bool allowMatchingMetadataAttribute) + { + + switch (name) + { + case nameof(global::MyEnum.First): + value = global::MyEnum.First; + return true; + case nameof(global::MyEnum.Second): + value = global::MyEnum.Second; + return true; + case nameof(global::MyEnum.Third): + value = global::MyEnum.Third; + return true; + case string s when int.TryParse(name, out var val): + value = (global::MyEnum)val; + return true; + default: + value = default; + return false; + } + } + +#if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_1 && !NETCOREAPP1_0 + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// An object of type whose + /// value is represented by + public static global::MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name) + => TryParse(name, out var value, false, false) ? value : ThrowValueNotFound(name.ToString()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// An object of type whose + /// value is represented by + public static global::MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + bool ignoreCase) + => TryParse(name, out var value, ignoreCase, false) ? value : ThrowValueNotFound(name.ToString()); + + /// + /// Converts the string representation of the name or numeric value of + /// an to the equivalent instance. + /// + /// The case-sensitive string representation of the enumeration name or underlying value to convert + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// An object of type whose + /// value is represented by + public static global::MyEnum Parse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => TryParse(name, out var value, ignoreCase, allowMatchingMetadataAttribute) ? value : ThrowValueNotFound(name.ToString()); + + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::MyEnum value) + => TryParse(name, out value, false, false); + + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::MyEnum value, + bool ignoreCase) + => TryParse(name, out value, ignoreCase, false); + + /// + /// Converts the span representation of the name or numeric value of + /// an to the equivalent instance. + /// The return value indicates whether the conversion succeeded. + /// + /// The span representation of the enumeration name or underlying value to convert + /// When this method returns, contains an object of type + /// whose + /// value is represented by if the parse operation succeeds. + /// If the parse operation fails, contains the default value of the underlying type + /// of . This parameter is passed uninitialized. + /// true to read value in case insensitive mode; false to read value in case sensitive mode. + /// If true, considers the value included in metadata attributes such as + /// [Display] attribute when parsing, otherwise only considers the member names. + /// true if the value parameter was converted successfully; otherwise, false. + public static bool TryParse( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::MyEnum result, + bool ignoreCase, + bool allowMatchingMetadataAttribute) + => ignoreCase + ? TryParseIgnoreCase(in name, out result, allowMatchingMetadataAttribute) + : TryParseWithCase(in name, out result, allowMatchingMetadataAttribute); + + private static bool TryParseIgnoreCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::MyEnum result, + bool allowMatchingMetadataAttribute) + { + switch (name) + { + case ReadOnlySpan current when current.Equals(nameof(global::MyEnum.First).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase): + result = global::MyEnum.First; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::MyEnum.Second).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase): + result = global::MyEnum.Second; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::MyEnum.Third).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase): + result = global::MyEnum.Third; + return true; + case ReadOnlySpan current when int.TryParse(name, out var numericResult): + result = (global::MyEnum)numericResult; + return true; + default: + result = default; + return false; + } + } + + private static bool TryParseWithCase( +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + in ReadOnlySpan name, + out global::MyEnum result, + bool allowMatchingMetadataAttribute) + { + switch (name) + { + case ReadOnlySpan current when current.Equals(nameof(global::MyEnum.First).AsSpan(), global::System.StringComparison.Ordinal): + result = global::MyEnum.First; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::MyEnum.Second).AsSpan(), global::System.StringComparison.Ordinal): + result = global::MyEnum.Second; + return true; + case ReadOnlySpan current when current.Equals(nameof(global::MyEnum.Third).AsSpan(), global::System.StringComparison.Ordinal): + result = global::MyEnum.Third; + return true; + case ReadOnlySpan current when int.TryParse(name, out var numericResult): + result = (global::MyEnum)numericResult; + return true; + default: + result = default; + return false; + } + } +#endif + + /// + /// Retrieves an array of the values of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the values defined in + public static global::MyEnum[] GetValues() + { + return new[] + { + global::MyEnum.First, + global::MyEnum.Second, + global::MyEnum.Third, + }; + } + + /// + /// Retrieves an array of the names of the members defined in + /// . + /// Note that this returns a new array with every invocation, so + /// should be cached if appropriate. + /// + /// An array of the names of the members defined in + public static string[] GetNames() + { + return new[] + { + nameof(global::MyEnum.First), + nameof(global::MyEnum.Second), + nameof(global::MyEnum.Third), + }; + } + } +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums + +//------------------------------------------------------------------------------ +// +// This code was generated by the NetEscapades.EnumGenerators source generator +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#nullable enable +namespace System.Runtime.CompilerServices +{ + // this type is needed by the compiler to implement interceptors - it doesn't need to + // come from the runtime itself, though + + [global::System.Diagnostics.Conditional("DEBUG")] // not needed post-build, so: evaporate + [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] + sealed file class InterceptsLocationAttribute : global::System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + _ = version; + _ = data; + } + } +} + +#pragma warning disable CS0612 // Ignore usages of obsolete members or enums +#pragma warning disable CS0618 // Ignore usages of obsolete members or enums +namespace NetEscapades.EnumGenerators +{ + static file class EnumInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(24,28) + public static string MyEnumExtensionsToString(this global::System.Enum value) + => global::MyEnumExtensions.ToStringFast((global::MyEnum)value); + + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(20,28) + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(21,34) + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(22,29) + [global::System.Runtime.CompilerServices.InterceptsLocation(123, "REDACTED")] // Program.cs(23,27) + public static bool MyEnumExtensionsHasFlag(this global::System.Enum value, global::System.Enum flag) + => global::MyEnumExtensions.HasFlagFast((global::MyEnum)value, (global::MyEnum)flag); + + } +} +#pragma warning restore CS0612 // Ignore usages of obsolete members or enums +#pragma warning restore CS0618 // Ignore usages of obsolete members or enums + +] \ No newline at end of file