|
@@ -0,0 +1,452 @@
|
|
|
+using System;
|
|
|
+using System.Diagnostics.CodeAnalysis;
|
|
|
+using System.Text;
|
|
|
+using System.Threading;
|
|
|
+using Microsoft.CodeAnalysis;
|
|
|
+using Microsoft.CodeAnalysis.Text;
|
|
|
+using Terminal.Gui.Analyzers.Internal.Attributes;
|
|
|
+using Terminal.Gui.Analyzers.Internal.Constants;
|
|
|
+
|
|
|
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// Incremental code generator for enums decorated with <see cref="GenerateEnumExtensionMethodsAttribute"/>.
|
|
|
+/// </summary>
|
|
|
+[SuppressMessage ("CodeQuality", "IDE0079", Justification = "Suppressions here are intentional and the warnings they disable are just noise.")]
|
|
|
+[Generator (LanguageNames.CSharp)]
|
|
|
+public sealed class EnumExtensionMethodsIncrementalGenerator : IIncrementalGenerator
|
|
|
+{
|
|
|
+ private const string ExtensionsForEnumTypeAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{ExtensionsForEnumTypeAttributeName}";
|
|
|
+ private const string ExtensionsForEnumTypeAttributeName = "ExtensionsForEnumTypeAttribute";
|
|
|
+ private const string GeneratorAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{GeneratorAttributeName}";
|
|
|
+ private const string GeneratorAttributeName = nameof (GenerateEnumExtensionMethodsAttribute);
|
|
|
+
|
|
|
+ /// <summary>Fully-qualified symbol name format without the "global::" prefix.</summary>
|
|
|
+ private static readonly SymbolDisplayFormat FullyQualifiedSymbolDisplayFormatWithoutGlobal =
|
|
|
+ SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle (SymbolDisplayGlobalNamespaceStyle.Omitted);
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ /// <remarks>
|
|
|
+ /// <para>
|
|
|
+ /// Basically, this method is called once by the compiler, and is responsible for wiring up
|
|
|
+ /// everything important about how source generation works.
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// See in-line comments for specifics of what's going on.
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// Note that <paramref name="context"/> is everything in the compilation,
|
|
|
+ /// except for code generated by this generator or generators which have not yet executed.<br/>
|
|
|
+ /// The methods registered to perform generation get called on-demand by the host (the IDE,
|
|
|
+ /// compiler, etc), sometimes as often as every single keystroke.
|
|
|
+ /// </para>
|
|
|
+ /// </remarks>
|
|
|
+ public void Initialize (IncrementalGeneratorInitializationContext context)
|
|
|
+ {
|
|
|
+ // Write out namespaces that may be used later. Harmless to declare them now and will avoid
|
|
|
+ // additional processing and potential omissions later on.
|
|
|
+ context.RegisterPostInitializationOutput (GenerateDummyNamespaces);
|
|
|
+
|
|
|
+ // This executes the delegate given to it immediately after Roslyn gets all set up.
|
|
|
+ //
|
|
|
+ // As written, this will result in the GenerateEnumExtensionMethodsAttribute code
|
|
|
+ // being added to the environment, so that it can be used without having to actually
|
|
|
+ // be declared explicitly in the target project.
|
|
|
+ // This is important, as it guarantees the type will exist and also guarantees it is
|
|
|
+ // defined exactly as the generator expects it to be defined.
|
|
|
+ context.RegisterPostInitializationOutput (GenerateAttributeSources);
|
|
|
+
|
|
|
+ // Next up, we define our pipeline.
|
|
|
+ // To do so, we create one or more IncrementalValuesProvider<T> objects, each of which
|
|
|
+ // defines on stage of analysis or generation as needed.
|
|
|
+ //
|
|
|
+ // Critically, these must be as fast and efficient as reasonably possible because,
|
|
|
+ // once the pipeline is registered, this stuff can get called A LOT.
|
|
|
+ //
|
|
|
+ // Note that declaring these doesn't really do much of anything unless they are given to the
|
|
|
+ // RegisterSourceOutput method at the end of this method.
|
|
|
+ //
|
|
|
+ // The delegates are not actually evaluated right here. That is triggered by changes being
|
|
|
+ // made to the source code.
|
|
|
+
|
|
|
+ // This provider grabs attributes that pass our filter and then creates lightweight
|
|
|
+ // metadata objects to be used in the final code generation step.
|
|
|
+ // It also preemptively removes any nulls from the collection before handing things off
|
|
|
+ // to the code generation logic.
|
|
|
+ IncrementalValuesProvider<EnumExtensionMethodsGenerationInfo?> enumGenerationInfos =
|
|
|
+ context
|
|
|
+ .SyntaxProvider
|
|
|
+
|
|
|
+ // This method is a highly-optimized (and highly-recommended) filter on the incoming
|
|
|
+ // code elements that only bothers to present code that is annotated with the specified
|
|
|
+ // attribute, by its fully-qualified name, as a string, which is the first parameter.
|
|
|
+ //
|
|
|
+ // Two delegates are passed to it, in the second and third parameters.
|
|
|
+ //
|
|
|
+ // The second parameter is a filter predicate taking each SyntaxNode that passes the
|
|
|
+ // name filter above, and then refines that result.
|
|
|
+ //
|
|
|
+ // It is critical that the filter predicate be as simple and fast as possible, as it
|
|
|
+ // will be called a ton, triggered by keystrokes or anything else that modifies code
|
|
|
+ // in or even related to (in either direction) the pre-filtered code.
|
|
|
+ // It should collect metadata only and not actually generate any code.
|
|
|
+ // It must return a boolean indicating whether the supplied SyntaxNode should be
|
|
|
+ // given to the transform delegate at all.
|
|
|
+ //
|
|
|
+ // The third parameter is the "transform" delegate.
|
|
|
+ // That one only runs when code is changed that passed both the attribute name filter
|
|
|
+ // and the filter predicate in the second parameter.
|
|
|
+ // It will be called for everything that passes both of those, so it can still happen
|
|
|
+ // a lot, but should at least be pretty close.
|
|
|
+ // In our case, it should be 100% accurate, since we're using OUR attribute, which can
|
|
|
+ // only be applied to enum types in the first place.
|
|
|
+ //
|
|
|
+ // That delegate is responsible for creating some sort of lightweight data structure
|
|
|
+ // which can later be used to generate the actual source code for output.
|
|
|
+ //
|
|
|
+ // THIS DELEGATE DOES NOT GENERATE CODE!
|
|
|
+ // However, it does need to return instances of the metadata class in use that are either
|
|
|
+ // null or complete enough to generate meaningful code from, later on.
|
|
|
+ //
|
|
|
+ // We then filter out any that were null with the .Where call at the end, so that we don't
|
|
|
+ // know or care about them when it's time to generate code.
|
|
|
+ //
|
|
|
+ // While the syntax of that .Where call is the same as LINQ, that is actually a
|
|
|
+ // highly-optimized implementation specifically for this use.
|
|
|
+ .ForAttributeWithMetadataName (
|
|
|
+ GeneratorAttributeFullyQualifiedName,
|
|
|
+ IsPotentiallyInterestingDeclaration,
|
|
|
+ GatherMetadataForCodeGeneration
|
|
|
+ )
|
|
|
+ .WithTrackingName ("CollectEnumMetadata")
|
|
|
+ .Where (static eInfo => eInfo is { });
|
|
|
+
|
|
|
+ // Finally, we wire up any IncrementalValuesProvider<T> instances above to the appropriate
|
|
|
+ // delegate that takes the SourceProductionContext that is current at run-time and an instance of
|
|
|
+ // our metadata type and takes appropriate action.
|
|
|
+ // Typically that means generating code from that metadata and adding it to the compilation via
|
|
|
+ // the received context object.
|
|
|
+ //
|
|
|
+ // As with everything else , the delegate will be invoked once for each item that passed
|
|
|
+ // all of the filters above, so we get to write that method from the perspective of a single
|
|
|
+ // enum type declaration.
|
|
|
+
|
|
|
+ context.RegisterSourceOutput (enumGenerationInfos, GenerateSourceFromGenerationInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static EnumExtensionMethodsGenerationInfo? GatherMetadataForCodeGeneration (
|
|
|
+ GeneratorAttributeSyntaxContext context,
|
|
|
+ CancellationToken cancellationToken
|
|
|
+ )
|
|
|
+ {
|
|
|
+ var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
|
|
|
+ cancellationToken.ThrowIfCancellationRequested ();
|
|
|
+
|
|
|
+ // If it's not an enum symbol, we don't care.
|
|
|
+ // EnumUnderlyingType is null for non-enums, so this validates it's an enum declaration.
|
|
|
+ if (context.TargetSymbol is not INamedTypeSymbol { EnumUnderlyingType: { } } namedSymbol)
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ INamespaceSymbol? enumNamespaceSymbol = namedSymbol.ContainingNamespace;
|
|
|
+
|
|
|
+ if (enumNamespaceSymbol is null or { IsGlobalNamespace: true })
|
|
|
+ {
|
|
|
+ // Explicitly choosing not to support enums in the global namespace.
|
|
|
+ // The corresponding analyzer will report this.
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ string enumName = namedSymbol.Name;
|
|
|
+
|
|
|
+ string enumNamespace = enumNamespaceSymbol.ToDisplayString (FullyQualifiedSymbolDisplayFormatWithoutGlobal);
|
|
|
+
|
|
|
+ TypeCode enumTypeCode = namedSymbol.EnumUnderlyingType.Name switch
|
|
|
+ {
|
|
|
+ "UInt32" => TypeCode.UInt32,
|
|
|
+ "Int32" => TypeCode.Int32,
|
|
|
+ _ => TypeCode.Empty
|
|
|
+ };
|
|
|
+
|
|
|
+ EnumExtensionMethodsGenerationInfo info = new (
|
|
|
+ enumNamespace,
|
|
|
+ enumName,
|
|
|
+ enumTypeCode
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!info.TryConfigure (namedSymbol, cts.Token))
|
|
|
+ {
|
|
|
+ cts.Cancel ();
|
|
|
+ cts.Token.ThrowIfCancellationRequested ();
|
|
|
+ }
|
|
|
+
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static void GenerateAttributeSources (IncrementalGeneratorPostInitializationContext postInitializationContext)
|
|
|
+ {
|
|
|
+ postInitializationContext
|
|
|
+ .AddSource (
|
|
|
+ $"{nameof (IExtensionsForEnumTypeAttributes)}.g.cs",
|
|
|
+ SourceText.From (
|
|
|
+ $$"""
|
|
|
+ // ReSharper disable All
|
|
|
+ {{Strings.Templates.AutoGeneratedCommentBlock}}
|
|
|
+ using System;
|
|
|
+
|
|
|
+ namespace {{Strings.AnalyzersAttributesNamespace}};
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Interface to simplify general enumeration of constructed generic types for
|
|
|
+ /// <see cref="ExtensionsForEnumTypeAttribute{TEnum}"/>
|
|
|
+ /// </summary>
|
|
|
+ {{Strings.Templates.AttributesForGeneratedInterfaces}}
|
|
|
+ public interface IExtensionsForEnumTypeAttributes
|
|
|
+ {
|
|
|
+ System.Type EnumType { get; }
|
|
|
+ }
|
|
|
+
|
|
|
+ """,
|
|
|
+ Encoding.UTF8));
|
|
|
+
|
|
|
+ postInitializationContext
|
|
|
+ .AddSource (
|
|
|
+ $"{nameof (AssemblyExtendedEnumTypeAttribute)}.g.cs",
|
|
|
+ SourceText.From (
|
|
|
+ $$"""
|
|
|
+ // ReSharper disable All
|
|
|
+ #nullable enable
|
|
|
+ {{Strings.Templates.AutoGeneratedCommentBlock}}
|
|
|
+
|
|
|
+ namespace {{Strings.AnalyzersAttributesNamespace}};
|
|
|
+
|
|
|
+ /// <summary>Assembly attribute declaring a known pairing of an <see langword="enum" /> type to an extension class.</summary>
|
|
|
+ /// <remarks>This attribute should only be written by internal source generators for Terminal.Gui. No other usage of any kind is supported.</remarks>
|
|
|
+ {{Strings.Templates.AttributesForGeneratedTypes}}
|
|
|
+ [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple = true)]
|
|
|
+ public sealed class {{nameof(AssemblyExtendedEnumTypeAttribute)}} : System.Attribute
|
|
|
+ {
|
|
|
+ /// <summary>Creates a new instance of <see cref="AssemblyExtendedEnumTypeAttribute" /> from the provided parameters.</summary>
|
|
|
+ /// <param name="enumType">The <see cref="System.Type" /> of an <see langword="enum" /> decorated with a <see cref="GenerateEnumExtensionMethodsAttribute" />.</param>
|
|
|
+ /// <param name="extensionClass">The <see cref="System.Type" /> of the <see langword="class" /> decorated with an <see cref="ExtensionsForEnumTypeAttribute{TEnum}" /> referring to the same type as <paramref name="enumType" />.</param>
|
|
|
+ public AssemblyExtendedEnumTypeAttribute (System.Type enumType, System.Type extensionClass)
|
|
|
+ {
|
|
|
+ EnumType = enumType;
|
|
|
+ ExtensionClass = extensionClass;
|
|
|
+ }
|
|
|
+ /// <summary>An <see langword="enum" /> type that has been extended by Terminal.Gui source generators.</summary>
|
|
|
+ public System.Type EnumType { get; init; }
|
|
|
+ /// <summary>A class containing extension methods for <see cref="EnumType"/>.</summary>
|
|
|
+ public System.Type ExtensionClass { get; init; }
|
|
|
+ /// <inheritdoc />
|
|
|
+ public override string ToString () => $"{EnumType.Name},{ExtensionClass.Name}";
|
|
|
+ }
|
|
|
+
|
|
|
+ """,
|
|
|
+ Encoding.UTF8));
|
|
|
+
|
|
|
+ postInitializationContext
|
|
|
+ .AddSource (
|
|
|
+ $"{GeneratorAttributeFullyQualifiedName}.g.cs",
|
|
|
+ SourceText.From (
|
|
|
+ $$"""
|
|
|
+ {{Strings.Templates.StandardHeader}}
|
|
|
+
|
|
|
+ namespace {{Strings.AnalyzersAttributesNamespace}};
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Used to enable source generation of a common set of extension methods for enum types.
|
|
|
+ /// </summary>
|
|
|
+ {{Strings.Templates.AttributesForGeneratedTypes}}
|
|
|
+ [System.AttributeUsageAttribute (System.AttributeTargets.Enum)]
|
|
|
+ public sealed class {{GeneratorAttributeName}} : Attribute
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// The name of the generated static class.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// If unspecified, null, empty, or only whitespace, defaults to the name of the enum plus "Extensions".<br/>
|
|
|
+ /// No other validation is performed, so illegal values will simply result in compiler errors.
|
|
|
+ /// <para>
|
|
|
+ /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
|
|
|
+ /// </para>
|
|
|
+ /// </remarks>
|
|
|
+ public string? ClassName { get; set; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The namespace in which to place the generated static class containing the extension methods.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// If unspecified, null, empty, or only whitespace, defaults to the namespace of the enum.<br/>
|
|
|
+ /// No other validation is performed, so illegal values will simply result in compiler errors.
|
|
|
+ /// <para>
|
|
|
+ /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
|
|
|
+ /// </para>
|
|
|
+ /// </remarks>
|
|
|
+ public string? ClassNamespace { get; set; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Whether to generate a fast, zero-allocation, non-boxing, and reflection-free alternative to the built-in
|
|
|
+ /// <see cref="Enum.HasFlag"/> method.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// <para>
|
|
|
+ /// Default: false
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// If the enum is not decorated with <see cref="Flags"/>, this option has no effect.
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// If multiple members have the same value, the first member with that value will be used and subsequent members
|
|
|
+ /// with the same value will be skipped.
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// Overloads taking the enum type itself as well as the underlying type of the enum will be generated, enabling
|
|
|
+ /// avoidance of implicit or explicit cast overhead.
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
|
|
|
+ /// </para>
|
|
|
+ /// </remarks>
|
|
|
+ public bool FastHasFlags { get; set; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Whether to generate a fast, zero-allocation, and reflection-free alternative to the built-in
|
|
|
+ /// <see cref="Enum.IsDefined"/> method,
|
|
|
+ /// using a switch expression as a hard-coded reverse mapping of numeric values to explicitly-named members.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// <para>
|
|
|
+ /// Default: true
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// If multiple members have the same value, the first member with that value will be used and subsequent members
|
|
|
+ /// with the same value will be skipped.
|
|
|
+ /// </para>
|
|
|
+ /// <para>
|
|
|
+ /// As with <see cref="Enum.IsDefined"/> the source generator only considers explicitly-named members.<br/>
|
|
|
+ /// Generation of values which represent valid bitwise combinations of members of enums decorated with
|
|
|
+ /// <see cref="Flags"/> is not affected by this property.
|
|
|
+ /// </para>
|
|
|
+ /// </remarks>
|
|
|
+ public bool FastIsDefined { get; init; } = true;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a <see langword="bool"/> value indicating if this <see cref="GenerateEnumExtensionMethodsAttribute"/> instance
|
|
|
+ /// contains default values only. See <see href="#remarks">remarks</see> of this method or documentation on properties of this type for details.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>
|
|
|
+ /// A <see langword="bool"/> value indicating if all property values are default for this
|
|
|
+ /// <see cref="GenerateEnumExtensionMethodsAttribute"/> instance.
|
|
|
+ /// </returns>
|
|
|
+ /// <remarks>
|
|
|
+ /// Default values that will result in a <see langword="true"/> return value are:<br/>
|
|
|
+ /// <see cref="FastIsDefined"/> && !<see cref="FastHasFlags"/> && <see cref="ClassName"/>
|
|
|
+ /// <see langword="is"/> <see langword="null"/> && <see cref="ClassNamespace"/> <see langword="is"/>
|
|
|
+ /// <see langword="null"/>
|
|
|
+ /// </remarks>
|
|
|
+ public override bool IsDefaultAttribute ()
|
|
|
+ {
|
|
|
+ return FastIsDefined
|
|
|
+ && !FastHasFlags
|
|
|
+ && ClassName is null
|
|
|
+ && ClassNamespace is null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ """,
|
|
|
+ Encoding.UTF8));
|
|
|
+
|
|
|
+ postInitializationContext
|
|
|
+ .AddSource (
|
|
|
+ $"{ExtensionsForEnumTypeAttributeFullyQualifiedName}.g.cs",
|
|
|
+ SourceText.From (
|
|
|
+ $$"""
|
|
|
+ // ReSharper disable RedundantNameQualifier
|
|
|
+ // ReSharper disable RedundantNullableDirective
|
|
|
+ // ReSharper disable UnusedType.Global
|
|
|
+ {{Strings.Templates.AutoGeneratedCommentBlock}}
|
|
|
+ #nullable enable
|
|
|
+
|
|
|
+ namespace {{Strings.AnalyzersAttributesNamespace}};
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Attribute written by the source generator for enum extension classes, for easier analysis and reflection.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// Properties are just convenient shortcuts to properties of <typeparamref name="TEnum"/>.
|
|
|
+ /// </remarks>
|
|
|
+ {{Strings.Templates.AttributesForGeneratedTypes}}
|
|
|
+ [System.AttributeUsageAttribute (System.AttributeTargets.Class | System.AttributeTargets.Interface)]
|
|
|
+ public sealed class {{ExtensionsForEnumTypeAttributeName}}<TEnum>: System.Attribute, IExtensionsForEnumTypeAttributes where TEnum : struct, Enum
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// The namespace-qualified name of <typeparamref name="TEnum"/>.
|
|
|
+ /// </summary>
|
|
|
+ public string EnumFullName => EnumType.FullName!;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The unqualified name of <typeparamref name="TEnum"/>.
|
|
|
+ /// </summary>
|
|
|
+ public string EnumName => EnumType.Name;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The namespace containing <typeparamref name="TEnum"/>.
|
|
|
+ /// </summary>
|
|
|
+ public string EnumNamespace => EnumType.Namespace!;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The <see cref="Type"/> given by <see langword="typeof"/>(<typeparamref name="TEnum"/>).
|
|
|
+ /// </summary>
|
|
|
+ public Type EnumType => typeof (TEnum);
|
|
|
+ }
|
|
|
+
|
|
|
+ """,
|
|
|
+ Encoding.UTF8));
|
|
|
+ }
|
|
|
+
|
|
|
+ [SuppressMessage ("Roslynator", "RCS1267", Justification = "Intentionally used so that Spans are used.")]
|
|
|
+ private static void GenerateDummyNamespaces (IncrementalGeneratorPostInitializationContext postInitializeContext)
|
|
|
+ {
|
|
|
+ postInitializeContext.AddSource (
|
|
|
+ string.Concat (Strings.InternalAnalyzersNamespace, "Namespaces.g.cs"),
|
|
|
+ SourceText.From (Strings.Templates.DummyNamespaceDeclarations, Encoding.UTF8));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void GenerateSourceFromGenerationInfo (SourceProductionContext context, EnumExtensionMethodsGenerationInfo? enumInfo)
|
|
|
+ {
|
|
|
+ // Just in case we still made it this far with a null...
|
|
|
+ if (enumInfo is not { })
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ CodeWriter writer = new (enumInfo);
|
|
|
+
|
|
|
+ context.AddSource ($"{enumInfo.FullyQualifiedClassName}.g.cs", writer.GenerateSourceText ());
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Returns true if <paramref name="syntaxNode"/> is an EnumDeclarationSyntax
|
|
|
+ /// whose parent is a NamespaceDeclarationSyntax, FileScopedNamespaceDeclarationSyntax, or a
|
|
|
+ /// (Class|Struct)DeclarationSyntax.<br/>
|
|
|
+ /// Additional filtering is performed in later stages.
|
|
|
+ /// </summary>
|
|
|
+ private static bool IsPotentiallyInterestingDeclaration (SyntaxNode syntaxNode, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ cancellationToken.ThrowIfCancellationRequested ();
|
|
|
+
|
|
|
+ return syntaxNode is
|
|
|
+ {
|
|
|
+ RawKind: 8858, //(int)SyntaxKind.EnumDeclaration,
|
|
|
+ Parent.RawKind: 8845 //(int)SyntaxKind.FileScopedNamespaceDeclaration
|
|
|
+ or 8842 //(int)SyntaxKind.NamespaceDeclaration
|
|
|
+ or 8855 //(int)SyntaxKind.ClassDeclaration
|
|
|
+ or 8856 //(int)SyntaxKind.StructDeclaration
|
|
|
+ or 9068 //(int)SyntaxKind.RecordStructDeclaration
|
|
|
+ or 9063 //(int)SyntaxKind.RecordDeclaration
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|