From c40fc8ed7f9d51ce0940ffe1cad9ffbc9e836d7d Mon Sep 17 00:00:00 2001 From: Alexander Wiedemann Date: Tue, 30 Jul 2024 16:57:56 +0200 Subject: [PATCH] - use infos already present GeneratorAttributeSyntaxContext to optimze pipeline steps --- .../EnumTypeGenerator.cs | 17 +- .../GeneratorHelper.cs | 28 ++- .../ResultType/Generator.cs | 10 +- .../ResultType/Parser.cs | 3 +- .../ResultType/ResultTypeSchema.cs | 8 +- .../ResultTypeGenerator.cs | 42 +++-- .../UnionType/Parser.cs | 174 +++++++----------- .../UnionTypeGenerator.cs | 16 +- 8 files changed, 144 insertions(+), 154 deletions(-) diff --git a/Source/FunicularSwitch.Generators/EnumTypeGenerator.cs b/Source/FunicularSwitch.Generators/EnumTypeGenerator.cs index 08237c8..db72539 100644 --- a/Source/FunicularSwitch.Generators/EnumTypeGenerator.cs +++ b/Source/FunicularSwitch.Generators/EnumTypeGenerator.cs @@ -130,24 +130,11 @@ static IEnumerable GetSymbolInfosForExtendEnumTypeAttribute(Attr static (EnumCaseOrder caseOrder, ExtensionAccessibility visibility) GetAttributeNamedArguments( AttributeData extendEnumTypesAttribute) { - var caseOrder = GetEnumNamedArgument(extendEnumTypesAttribute, "CaseOrder", EnumCaseOrder.AsDeclared); - var visibility = GetEnumNamedArgument(extendEnumTypesAttribute, "Accessibility", ExtensionAccessibility.Public); + var caseOrder = extendEnumTypesAttribute.GetEnumNamedArgument("CaseOrder", EnumCaseOrder.AsDeclared); + var visibility = extendEnumTypesAttribute.GetEnumNamedArgument("Accessibility", ExtensionAccessibility.Public); return (caseOrder, visibility); } - static T GetEnumNamedArgument(AttributeData attributeData, string name, T defaultValue) where T : struct - { - foreach (var kv in attributeData.NamedArguments) - { - if (kv.Key != name) - continue; - - return (T)(object)((int)kv.Value.Value!); - } - - return defaultValue; - } - static IEnumerable GetSymbolInfosForExtendEnumTypesAttribute(AttributeData extendEnumTypesAttribute) { var attributeSymbol = extendEnumTypesAttribute.AttributeClass!; diff --git a/Source/FunicularSwitch.Generators/GeneratorHelper.cs b/Source/FunicularSwitch.Generators/GeneratorHelper.cs index 09fcf73..dc83ec6 100644 --- a/Source/FunicularSwitch.Generators/GeneratorHelper.cs +++ b/Source/FunicularSwitch.Generators/GeneratorHelper.cs @@ -26,17 +26,29 @@ static class GeneratorHelper return hasAttribute ? classDeclarationSyntax : null; } - public static T GetNamedEnumAttributeArgument(this AttributeSyntax attribute, string name, T defaultValue) where T : struct + public static T GetEnumNamedArgument(this AttributeData attributeData, string name, T defaultValue) where T : struct { - var memberAccess = attribute.ArgumentList?.Arguments - .Where(a => a.NameEquals?.Name.ToString() == name) - .Select(a => a.Expression) - .OfType() - .FirstOrDefault(); + foreach (var kv in attributeData.NamedArguments) + { + if (kv.Key != name) + continue; + + return (T)(object)((int)kv.Value.Value!); + } + + return defaultValue; + } + public static T GetNamedArgument(this AttributeData attributeData, string name, T defaultValue) + { + foreach (var kv in attributeData.NamedArguments) + { + if (kv.Key != name) + continue; - if (memberAccess == null) return defaultValue; + return (T)kv.Value.Value!; + } - return (T)Enum.Parse(typeof(T), memberAccess.Name.ToString()); + return defaultValue; } } \ No newline at end of file diff --git a/Source/FunicularSwitch.Generators/ResultType/Generator.cs b/Source/FunicularSwitch.Generators/ResultType/Generator.cs index 2eea67e..11d3646 100644 --- a/Source/FunicularSwitch.Generators/ResultType/Generator.cs +++ b/Source/FunicularSwitch.Generators/ResultType/Generator.cs @@ -12,6 +12,7 @@ static class Generator public static IEnumerable<(string filename, string source)> Emit( ResultTypeSchema resultTypeSchema, + SymbolWrapper defaultErrorType, MergeMethod? mergeErrorMethod, ExceptionToErrorMethod? exceptionToErrorMethod, Action reportDiagnostic, @@ -24,16 +25,17 @@ static class Generator reportDiagnostic(Diagnostics.ResultTypeInGlobalNamespace($"Result type {resultTypeName} is placed in global namespace, this is not supported. Please put {resultTypeName} into non empty namespace.", resultTypeSchema.ResultTypeLocation?.ToLocation() ?? Location.None)); yield break; } - - var isValueType = resultTypeSchema.ErrorType.Symbol.IsValueType; - var errorTypeNamespace = resultTypeSchema.ErrorType.Symbol.GetFullNamespace(); + + var errorTypeSymbol = resultTypeSchema.ErrorType ?? defaultErrorType; + var isValueType = errorTypeSymbol.Symbol.IsValueType; + var errorTypeNamespace = errorTypeSymbol.Symbol.GetFullNamespace(); string Replace(string code, IReadOnlyCollection additionalNamespaces, string genericTypeParameterNameForHandleExceptions) { code = code .Replace($"namespace {TemplateNamespace}", $"namespace {resultTypeNamespace}") .Replace(TemplateResultTypeName, resultTypeName) - .Replace(TemplateErrorTypeName, resultTypeSchema.ErrorType.Symbol.Name); + .Replace(TemplateErrorTypeName, errorTypeSymbol.Symbol.Name); if (resultTypeSchema.IsInternal) code = code diff --git a/Source/FunicularSwitch.Generators/ResultType/Parser.cs b/Source/FunicularSwitch.Generators/ResultType/Parser.cs index d2a2dd0..08d3763 100644 --- a/Source/FunicularSwitch.Generators/ResultType/Parser.cs +++ b/Source/FunicularSwitch.Generators/ResultType/Parser.cs @@ -7,7 +7,8 @@ namespace FunicularSwitch.Generators.ResultType; static class Parser { - public static GenerationResult GetResultTypeSchema(ClassDeclarationSyntax resultTypeClass, Compilation compilation, CancellationToken cancellationToken) + public static GenerationResult GetResultTypeSchema( + ClassDeclarationSyntax resultTypeClass, Compilation compilation, CancellationToken cancellationToken) { var semanticModel = compilation.GetSemanticModel(resultTypeClass.SyntaxTree); diff --git a/Source/FunicularSwitch.Generators/ResultType/ResultTypeSchema.cs b/Source/FunicularSwitch.Generators/ResultType/ResultTypeSchema.cs index f964b76..fd864a3 100644 --- a/Source/FunicularSwitch.Generators/ResultType/ResultTypeSchema.cs +++ b/Source/FunicularSwitch.Generators/ResultType/ResultTypeSchema.cs @@ -7,15 +7,15 @@ namespace FunicularSwitch.Generators.ResultType; sealed class ResultTypeSchema( ClassDeclarationSyntax resultType, - INamedTypeSymbol errorType) + INamedTypeSymbol? errorType) { - public SymbolWrapper ErrorType { get; } = new (errorType); + public SymbolWrapper? ErrorType { get; } = errorType != null ? new (errorType) : null; public LocationInfo? ResultTypeLocation { get; } = LocationInfo.CreateFrom(resultType.GetLocation()); public bool IsInternal { get; } = !resultType.Modifiers.HasModifier(SyntaxKind.PublicKeyword); public QualifiedTypeName ResultTypeName { get; } = resultType.QualifiedName(); public string? ResultTypeNamespace { get; } = resultType.GetContainingNamespace(); - bool Equals(ResultTypeSchema other) => ErrorType.Equals(other.ErrorType) && IsInternal == other.IsInternal && ResultTypeName == other.ResultTypeName && ResultTypeNamespace == other.ResultTypeNamespace; + bool Equals(ResultTypeSchema other) => Equals(ErrorType, other.ErrorType) && IsInternal == other.IsInternal && ResultTypeName == other.ResultTypeName && ResultTypeNamespace == other.ResultTypeNamespace; public override bool Equals(object? obj) => ReferenceEquals(this, obj) || obj is ResultTypeSchema other && Equals(other); @@ -23,7 +23,7 @@ public override int GetHashCode() { unchecked { - var hashCode = ErrorType.GetHashCode(); + var hashCode = ErrorType?.GetHashCode() ?? 0; hashCode = (hashCode * 397) ^ IsInternal.GetHashCode(); hashCode = (hashCode * 397) ^ ResultTypeName.GetHashCode(); hashCode = (hashCode * 397) ^ (ResultTypeNamespace != null ? ResultTypeNamespace.GetHashCode() : 0); diff --git a/Source/FunicularSwitch.Generators/ResultTypeGenerator.cs b/Source/FunicularSwitch.Generators/ResultTypeGenerator.cs index a8047c9..2043ec9 100644 --- a/Source/FunicularSwitch.Generators/ResultTypeGenerator.cs +++ b/Source/FunicularSwitch.Generators/ResultTypeGenerator.cs @@ -21,17 +21,23 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var resultTypeClasses = context.SyntaxProvider - .CreateSyntaxProvider( - predicate: static (s, _) => s.IsTypeDeclarationWithAttributes(), + .ForAttributeWithMetadataName( + ResultTypeAttribute, + predicate: static (s, _) => true, transform: static (ctx, cancellationToken) => { //TODO: support record result types one day - if (GeneratorHelper.GetSemanticTargetForGeneration(ctx, ResultTypeAttribute) is not ClassDeclarationSyntax resultTypeClass) + if (ctx.TargetSymbol is not INamedTypeSymbol n || n.IsRecord) return GenerationResult.Empty; - var schema = Parser.GetResultTypeSchema(resultTypeClass - , ctx.SemanticModel.Compilation, cancellationToken); - return schema; + var resultClass = (ClassDeclarationSyntax)ctx.TargetNode; + var errorTypeSymbol = (INamedTypeSymbol?)(!ctx.Attributes[0].NamedArguments.IsEmpty + ? ctx.Attributes[0].NamedArguments[0].Value.Value! + : !ctx.Attributes[0].ConstructorArguments.IsEmpty + ? ctx.Attributes[0].ConstructorArguments[0].Value + : null); + + return new ResultTypeSchema(resultClass, errorTypeSymbol); }); var compilationAndClasses = context.CompilationProvider @@ -43,8 +49,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return GenerationResult.Create( ( - mergeMetdhods: mergeMethods.Values.ToImmutableArray().AsEquatableArray(), - exceptionToErrorMethods: exceptionToErrorMethods.Values.ToImmutableArray().AsEquatableArray() + mergeMethods: mergeMethods.Values.ToImmutableArray().AsEquatableArray(), + exceptionToErrorMethods: exceptionToErrorMethods.Values.ToImmutableArray().AsEquatableArray(), + stringSymbol: SymbolWrapper.Create(compilation.GetTypeByMetadataName("System.String")!) ), diagnostics.Select(d => new DiagnosticInfo(d)).ToImmutableArray(), true ); @@ -57,7 +64,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } static void Execute( - GenerationResult<(EquatableArray mergeMethods, EquatableArray exceptionToErrorMethods)> resultTypeMethods, + GenerationResult<(EquatableArray mergeMethods, EquatableArray exceptionToErrorMethods, SymbolWrapper stringSymbol)> resultTypeMethods, ImmutableArray> resultTypeClassesResult, SourceProductionContext context) { foreach (var diagnosticInfo in resultTypeClassesResult @@ -73,10 +80,19 @@ static void Execute( if (resultTypeSchemata.IsDefaultOrEmpty) return; var generated = resultTypeSchemata - .SelectMany(r => Generator.Emit(r, - resultTypeMethods.Value.mergeMethods.FirstOrDefault(m => m.FullErrorTypeName == r.ErrorType.FullNameWithNamespace), - resultTypeMethods.Value.exceptionToErrorMethods.FirstOrDefault(e => e.ErrorTypeName == r.ErrorType.FullNameWithNamespace), - context.ReportDiagnostic, context.CancellationToken)).ToImmutableArray(); + .SelectMany(r => + { + var defaultErrorType = resultTypeMethods.Value.stringSymbol; + var errorTypeSymbol = r.ErrorType ?? defaultErrorType; + + return Generator.Emit(r, + defaultErrorType, + resultTypeMethods.Value.mergeMethods.FirstOrDefault(m => + m.FullErrorTypeName == errorTypeSymbol.FullNameWithNamespace), + resultTypeMethods.Value.exceptionToErrorMethods.FirstOrDefault(e => + e.ErrorTypeName == errorTypeSymbol.FullNameWithNamespace), + context.ReportDiagnostic, context.CancellationToken); + }).ToImmutableArray(); foreach (var (filename, source) in generated) context.AddSource(filename, source); } diff --git a/Source/FunicularSwitch.Generators/UnionType/Parser.cs b/Source/FunicularSwitch.Generators/UnionType/Parser.cs index 66d4b20..99f4ac5 100644 --- a/Source/FunicularSwitch.Generators/UnionType/Parser.cs +++ b/Source/FunicularSwitch.Generators/UnionType/Parser.cs @@ -9,14 +9,13 @@ namespace FunicularSwitch.Generators.UnionType; static class Parser { - public static GenerationResult GetUnionTypeSchema(Compilation compilation, - CancellationToken cancellationToken, BaseTypeDeclarationSyntax unionTypeClass) + public static GenerationResult GetUnionTypeSchema(Compilation compilation, + CancellationToken cancellationToken, + BaseTypeDeclarationSyntax unionTypeClass, + INamedTypeSymbol unionTypeSymbol, + AttributeData unionTypeAttribute) { var semanticModel = compilation.GetSemanticModel(unionTypeClass.SyntaxTree); - var unionTypeSymbol = semanticModel.GetDeclaredSymbol(unionTypeClass); - - if (unionTypeSymbol == null) //TODO: report diagnostics - return GenerationResult.Empty; var fullTypeName = unionTypeSymbol.FullTypeNameWithNamespace(); var acc = unionTypeSymbol.DeclaredAccessibility; @@ -26,51 +25,46 @@ public static GenerationResult GetUnionTypeSchema(Compilation c return Error(diag); } - var attribute = unionTypeClass.AttributeLists - .SelectMany(l => l.Attributes) - .First(a => a.GetAttributeFullName(semanticModel) == UnionTypeGenerator.UnionTypeAttribute); + var caseOrder = unionTypeAttribute.GetEnumNamedArgument("CaseOrder", CaseOrder.Alphabetic); + var staticFactoryMethods = unionTypeAttribute.GetNamedArgument("StaticFactoryMethods", true); + - var caseOrderResult = TryGetCaseOrder(attribute); + var fullNamespace = unionTypeSymbol.GetFullNamespace(); - return caseOrderResult.Bind(t => + var derivedTypes = compilation.SyntaxTrees.SelectMany(syntaxTree => { - var fullNamespace = unionTypeSymbol.GetFullNamespace(); + var root = syntaxTree.GetRoot(cancellationToken); + var treeSemanticModel = syntaxTree != unionTypeClass.SyntaxTree ? compilation.GetSemanticModel(syntaxTree) : semanticModel; - var derivedTypes = compilation.SyntaxTrees.SelectMany(syntaxTree => - { - var root = syntaxTree.GetRoot(cancellationToken); - var treeSemanticModel = syntaxTree != unionTypeClass.SyntaxTree ? compilation.GetSemanticModel(syntaxTree) : semanticModel; - - return FindConcreteDerivedTypesWalker.Get(root, unionTypeSymbol, treeSemanticModel); - }); - - var (caseOrder, staticFactoryMethods) = t; - var isPartial = unionTypeClass.Modifiers.HasModifier(SyntaxKind.PartialKeyword); - var generateFactoryMethods = isPartial /*&& unionTypeClass is not InterfaceDeclarationSyntax*/ && - staticFactoryMethods; - - return - ToOrderedCases(caseOrder, derivedTypes, compilation, generateFactoryMethods, unionTypeSymbol.Name) - .Map(cases => - new UnionTypeSchema( - Namespace: fullNamespace, - TypeName: unionTypeSymbol.Name, - FullTypeName: fullTypeName, - Cases: cases, - IsInternal: acc is Accessibility.NotApplicable or Accessibility.Internal, - IsPartial: isPartial, - TypeKind: unionTypeClass switch - { - RecordDeclarationSyntax => UnionTypeTypeKind.Record, - InterfaceDeclarationSyntax => UnionTypeTypeKind.Interface, - _ => UnionTypeTypeKind.Class - }, - StaticFactoryInfo: generateFactoryMethods - ? BuildFactoryInfo(unionTypeClass, compilation) - : null - )); + return FindConcreteDerivedTypesWalker.Get(root, unionTypeSymbol, treeSemanticModel); }); - + + + var isPartial = unionTypeClass.Modifiers.HasModifier(SyntaxKind.PartialKeyword); + var generateFactoryMethods = isPartial && staticFactoryMethods; + + return + ToOrderedCases(caseOrder, derivedTypes, compilation, generateFactoryMethods, unionTypeSymbol.Name) + .Map(cases => + new UnionTypeSchema( + Namespace: fullNamespace, + TypeName: unionTypeSymbol.Name, + FullTypeName: fullTypeName, + Cases: cases, + IsInternal: acc is Accessibility.NotApplicable or Accessibility.Internal, + IsPartial: isPartial, + TypeKind: unionTypeClass switch + { + RecordDeclarationSyntax => UnionTypeTypeKind.Record, + InterfaceDeclarationSyntax => UnionTypeTypeKind.Interface, + _ => UnionTypeTypeKind.Class + }, + StaticFactoryInfo: generateFactoryMethods + ? BuildFactoryInfo(unionTypeClass, compilation) + : null + )); + + static GenerationResult Error(Diagnostic diagnostic) => GenerationResult.Empty.AddDiagnostics(diagnostic); } @@ -100,52 +94,26 @@ static bool DiffersAndValid(string typeName, string candidate) => static StaticFactoryMethodsInfo BuildFactoryInfo(BaseTypeDeclarationSyntax unionTypeClass, Compilation compilation) { - var staticMethods = unionTypeClass.ChildNodes() - .OfType() - .Where(m => m.Modifiers.HasModifier(SyntaxKind.StaticKeyword)) - .Select(m => m.ToMemberInfo(m.Name(), compilation)) - .ToImmutableArray(); - - var staticFields = unionTypeClass.ChildNodes() - .SelectMany(s => s switch - { - FieldDeclarationSyntax f when f.Modifiers.HasModifier(SyntaxKind.StaticKeyword) => f.Declaration - .Variables.Select(v => v.Identifier.Text), - PropertyDeclarationSyntax p when p.Modifiers.HasModifier(SyntaxKind.StaticKeyword) => new[] - { - p.Name() - }, - _ => Array.Empty() - }) - .ToImmutableArray(); - - return new(staticMethods, staticFields, unionTypeClass.Modifiers.ToEquatableModifiers()); - } - - static GenerationResult<(CaseOrder caseOder, bool staticFactoryMethods)> TryGetCaseOrder(AttributeSyntax attribute) - { - var caseOrder = CaseOrder.Alphabetic; - var staticFactoryMethods = true; - - if ((attribute.ArgumentList?.Arguments.Count ?? 0) < 1) - return (caseOrder, staticFactoryMethods); - - var errors = ImmutableArray.Empty; - foreach (var attributeArgumentSyntax in attribute.ArgumentList!.Arguments) - { - var propertyName = attributeArgumentSyntax.NameEquals?.Name.Identifier.Text; - if (propertyName == nameof(CaseOrder) && attributeArgumentSyntax.Expression is MemberAccessExpressionSyntax m) - caseOrder = (CaseOrder)Enum.Parse(typeof(CaseOrder), m.Name.ToString()); - else if (propertyName == "StaticFactoryMethods" && attributeArgumentSyntax.Expression is LiteralExpressionSyntax lit) - staticFactoryMethods = bool.Parse(lit.Token.Text); - else + var staticMethods = unionTypeClass.ChildNodes() + .OfType() + .Where(m => m.Modifiers.HasModifier(SyntaxKind.StaticKeyword)) + .Select(m => m.ToMemberInfo(m.Name(), compilation)) + .ToImmutableArray(); + + var staticFields = unionTypeClass.ChildNodes() + .SelectMany(s => s switch { - var diagnostic = Diagnostics.InvalidUnionTypeAttributeUsage($"Unsupported usage: {attribute}", attribute.GetLocation()); - errors = errors.Add(diagnostic); - } - } + FieldDeclarationSyntax f when f.Modifiers.HasModifier(SyntaxKind.StaticKeyword) => f.Declaration + .Variables.Select(v => v.Identifier.Text), + PropertyDeclarationSyntax p when p.Modifiers.HasModifier(SyntaxKind.StaticKeyword) => new[] + { + p.Name() + }, + _ => Array.Empty() + }) + .ToImmutableArray(); - return new ((caseOrder, staticFactoryMethods), errors, true); + return new(staticMethods, staticFields, unionTypeClass.Modifiers.ToEquatableModifiers()); } static GenerationResult> ToOrderedCases(CaseOrder caseOrder, @@ -165,7 +133,7 @@ static GenerationResult> ToOrderedCases(CaseOrder ca var result = ordered.ToImmutableArray(); var errors = ImmutableArray.Empty; - + switch (caseOrder) { case CaseOrder.Alphabetic: @@ -203,16 +171,16 @@ static GenerationResult> ToOrderedCases(CaseOrder ca IEnumerable? constructors = null; if (getConstructors) { - constructors = d.node.ChildNodes() - .OfType() - .Select(c => c.ToMemberInfo(c.Identifier.Text, compilation)); - - if (d.node is TypeDeclarationSyntax { ParameterList: not null } typeDeclaration) - constructors = constructors.Concat(new[] - { - new MemberInfo(d.node.Name(), d.node.Modifiers.ToEquatableModifiers(), typeDeclaration.ParameterList.Parameters - .Select(p => p.ToParameterInfo(compilation)).ToImmutableArray()) - }); + constructors = d.node.ChildNodes() + .OfType() + .Select(c => c.ToMemberInfo(c.Identifier.Text, compilation)); + + if (d.node is TypeDeclarationSyntax { ParameterList: not null } typeDeclaration) + constructors = constructors.Concat(new[] + { + new MemberInfo(d.node.Name(), d.node.Modifiers.ToEquatableModifiers(), typeDeclaration.ParameterList.Parameters + .Select(p => p.ToParameterInfo(compilation)).ToImmutableArray()) + }); } var (parameterName, staticMethodName) = @@ -224,7 +192,7 @@ static GenerationResult> ToOrderedCases(CaseOrder ca parameterName: parameterName, staticFactoryMethodName: staticMethodName); }).ToImmutableArray(); - + return new(derived, errors, true); } } @@ -278,7 +246,7 @@ void CheckIsConcreteDerived(BaseTypeDeclarationSyntax node) var attribute = node.AttributeLists .SelectMany(l => l.Attributes) .FirstOrDefault(a => a.GetAttributeFullName(m_SemanticModel) == UnionTypeGenerator.UnionCaseAttribute); - + var caseIndex = TryGetCaseIndex(attribute); m_DerivedClasses.Add((symbol, node, caseIndex)); diff --git a/Source/FunicularSwitch.Generators/UnionTypeGenerator.cs b/Source/FunicularSwitch.Generators/UnionTypeGenerator.cs index 5fc2476..8580b14 100644 --- a/Source/FunicularSwitch.Generators/UnionTypeGenerator.cs +++ b/Source/FunicularSwitch.Generators/UnionTypeGenerator.cs @@ -22,14 +22,18 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .ForAttributeWithMetadataName( UnionTypeAttribute, predicate: static (_, _) => true, - transform: static (ctx, cancellationToken) => - Parser.GetUnionTypeSchema(ctx.SemanticModel.Compilation, cancellationToken, (BaseTypeDeclarationSyntax)ctx.TargetNode)) - .Select(static (target, _) => target); - - var compilationAndClasses = unionTypeClasses; + transform: static (context, cancellationToken) => + Parser.GetUnionTypeSchema( + context.SemanticModel.Compilation, + cancellationToken, + (BaseTypeDeclarationSyntax)context.TargetNode, + (INamedTypeSymbol)context.TargetSymbol, + context.Attributes[0] + ) + ); context.RegisterSourceOutput( - compilationAndClasses, + unionTypeClasses, static (spc, source) => Execute(source, spc)); }