Browse Source

Merge pull request #7935 from hez2010/master

SubtypesFactory generator
pull/7998/head
Max Katz 4 years ago
committed by GitHub
parent
commit
a0b6ec6add
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      Avalonia.sln
  2. 10
      build/SourceGenerators.props
  3. 40
      src/Avalonia.Base/Animation/Easings/Easing.cs
  4. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  5. 5
      src/Avalonia.Base/Input/KeyGesture.cs
  6. 1
      src/Avalonia.Base/Properties/AssemblyInfo.cs
  7. 19
      src/Avalonia.Base/Utilities/EnumHelper.cs
  8. 7
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs
  9. 17
      src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj
  10. 14
      src/Avalonia.SourceGenerator/IsExternalInit.cs
  11. 155
      src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs
  12. 17
      src/Shared/SourceGeneratorAttributes.cs

28
Avalonia.sln

@ -39,6 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs
src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}"
@ -111,6 +112,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\SharedVersion.props = build\SharedVersion.props
build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.props
build\SourceGenerators.props = build\SourceGenerators.props
build\SourceLink.props = build\SourceLink.props
build\System.Drawing.Common.props = build\System.Drawing.Common.props
build\System.Memory.props = build\System.Memory.props
@ -211,6 +213,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "sampl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}"
EndProject
Global
@ -1911,6 +1915,30 @@ Global
{CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.Build.0 = Release|Any CPU
{CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.Build.0 = Release|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.ActiveCfg = Release|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.Build.0 = Release|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU

10
build/SourceGenerators.props

@ -0,0 +1,10 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectReference
Include="$(MSBuildThisFileDirectory)/../src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
PrivateAssets="all" />
<Compile Include="$(MSBuildThisFileDirectory)/../src/Shared/SourceGeneratorAttributes.cs" />
</ItemGroup>
</Project>

40
src/Avalonia.Base/Animation/Easings/Easing.cs

@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using Avalonia.SourceGenerator;
namespace Avalonia.Animation.Easings
{
@ -10,14 +11,15 @@ namespace Avalonia.Animation.Easings
/// Base class for all Easing classes.
/// </summary>
[TypeConverter(typeof(EasingTypeConverter))]
public abstract class Easing : IEasing
public abstract partial class Easing : IEasing
{
/// <inheritdoc/>
public abstract double Ease(double progress);
static Dictionary<string, Type>? _easingTypes;
private const string Namespace = "Avalonia.Animation.Easings";
static readonly Type s_thisType = typeof(Easing);
[SubtypesFactory(typeof(Easing), Namespace)]
private static partial bool TryCreateEasingInstance(string type, [NotNullWhen(true)] out Easing? instance);
/// <summary>
/// Parses a Easing type string.
@ -26,34 +28,18 @@ namespace Avalonia.Animation.Easings
/// <returns>Returns the instance of the parsed type.</returns>
public static Easing Parse(string e)
{
#if NETSTANDARD2_0
if (e.Contains(","))
#else
if (e.Contains(','))
#endif
{
return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture));
}
if (_easingTypes == null)
{
_easingTypes = new Dictionary<string, Type>();
// Fetch the built-in easings.
var derivedTypes = typeof(Easing).Assembly.GetTypes()
.Where(p => p.Namespace == s_thisType.Namespace)
.Where(p => p.IsSubclassOf(s_thisType))
.Select(p => p);
foreach (var easingType in derivedTypes)
_easingTypes.Add(easingType.Name, easingType);
}
if (_easingTypes.ContainsKey(e))
{
var type = _easingTypes[e];
return (Easing)Activator.CreateInstance(type)!;
}
else
{
throw new FormatException($"Easing \"{e}\" was not found in {s_thisType.Namespace} namespace.");
}
return TryCreateEasingInstance(e, out var easing)
? easing
: throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace.");
}
}
}

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -16,4 +16,5 @@
<Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
<Import Project="..\..\build\DevAnalyzers.props" />
<Import Project="..\..\build\SourceGenerators.props" />
</Project>

5
src/Avalonia.Base/Input/KeyGesture.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Utilities;
namespace Avalonia.Input
{
@ -155,7 +156,7 @@ namespace Avalonia.Input
if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv))
return rv;
return (Key)Enum.Parse(typeof(Key), key, true);
return EnumHelper.Parse<Key>(key, true);
}
private static KeyModifiers ParseModifier(ReadOnlySpan<char> modifier)
@ -172,7 +173,7 @@ namespace Avalonia.Input
return KeyModifiers.Meta;
}
return (KeyModifiers)Enum.Parse(typeof(KeyModifiers), modifier.ToString(), true);
return EnumHelper.Parse<KeyModifiers>(modifier.ToString(), true);
}
private Key ResolveNumPadOperationKey(Key key)

1
src/Avalonia.Base/Properties/AssemblyInfo.cs

@ -21,6 +21,7 @@ using Avalonia.Metadata;
[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]

19
src/Avalonia.Base/Utilities/EnumHelper.cs

@ -0,0 +1,19 @@
using System;
namespace Avalonia.Utilities
{
internal class EnumHelper
{
#if NET6_0_OR_GREATER
public static T Parse<T>(ReadOnlySpan<char> key, bool ignoreCase) where T : struct
{
return Enum.Parse<T>(key, ignoreCase);
}
#else
public static T Parse<T>(string key, bool ignoreCase) where T : struct
{
return (T)Enum.Parse(typeof(T), key, ignoreCase);
}
#endif
}
}

7
src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs

@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Utilities;
using InputProtocol = Avalonia.Remote.Protocol.Input;
namespace Avalonia.DesignerSupport.Remote.HtmlTransport
@ -320,15 +321,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
? null
: modifiersText
.Split(',')
.Select(x => (InputProtocol.InputModifiers)Enum.Parse(
typeof(InputProtocol.InputModifiers), x, true))
.Select(x => EnumHelper.Parse<InputProtocol.InputModifiers>(x, true))
.ToArray();
private static InputProtocol.MouseButton ParseMouseButton(string buttonText) =>
string.IsNullOrWhiteSpace(buttonText)
? InputProtocol.MouseButton.None
: (InputProtocol.MouseButton)Enum.Parse(
typeof(InputProtocol.MouseButton), buttonText, true);
: EnumHelper.Parse<InputProtocol.MouseButton>(buttonText, true);
private static double ParseDouble(string text) =>
double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture);

17
src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
<Compile Include="..\Shared\SourceGeneratorAttributes.cs" />
</ItemGroup>
</Project>

14
src/Avalonia.SourceGenerator/IsExternalInit.cs

@ -0,0 +1,14 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Reserved to be used by the compiler for tracking metadata.
/// This class should not be used by developers in source code.
/// </summary>
[ExcludeFromCodeCoverage, DebuggerNonUserCode]
internal static class IsExternalInit
{
}
}

155
src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs

@ -0,0 +1,155 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Avalonia.SourceGenerator
{
[Generator(LanguageNames.CSharp)]
public class SubtypesFactoryGenerator : IIncrementalGenerator
{
private record struct MethodTarget(IMethodSymbol Method, string MethodDecl, ITypeSymbol BaseType, string Namespace);
private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName;
private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType)
{
return type.BaseType is not null && (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType) || IsSubtypeOf(type.BaseType, baseType));
}
private static void GenerateSubTypes(SourceProductionContext context, MethodTarget methodTarget, ImmutableArray<ITypeSymbol> types)
{
var (method, methodDecl, baseType, @namespace) = methodTarget;
var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray();
var type = method.ContainingType;
var isGeneric = type.TypeParameters.Length > 0;
var isClass = type.TypeKind == TypeKind.Class;
var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}";
var source = $@"using System;
using System.Collections.Generic;
namespace {method.ContainingNamespace}
{{
{typeDecl}
{{
{methodDecl}
{{
var hasMatch = false;
(hasMatch, {method.Parameters[1].Name}) = {method.Parameters[0].Name} switch
{{
{string.Join("\n", candidateTypes.Select(i => $" \"{i.Name}\" => (true, ({method.Parameters[1].Type})new {i}()),"))}
_ => (false, default({method.Parameters[1].Type}))
}};
return hasMatch;
}}
}}
}}";
context.AddSource($"{type}.{method.MetadataName}.gen.cs", source);
}
private static MethodTarget? PopulateMethodTargets(GeneratorSyntaxContext context, CancellationToken token)
{
token.ThrowIfCancellationRequested();
if (context.Node is MethodDeclarationSyntax method)
{
var attributes = method.AttributeLists.SelectMany(i => i.Attributes);
var semanticModel = context.SemanticModel;
foreach (var attribute in attributes)
{
var attributeTypeInfo = semanticModel.GetTypeInfo(attribute);
if (attributeTypeInfo.Type is null ||
attributeTypeInfo.Type.ToString() != s_attributeName ||
attribute.ArgumentList is null)
{
continue;
}
var arguments = attribute.ArgumentList.Arguments;
if (arguments.Count != 2)
{
continue;
}
if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr ||
arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax)
{
continue;
}
var type = semanticModel.GetTypeInfo(typeOfExpr.Type);
var ns = semanticModel.GetConstantValue(arguments[1].Expression);
var methodDeclInfo = semanticModel.GetDeclaredSymbol(method);
if (type.Type is not ITypeSymbol baseType ||
ns.HasValue is false ||
ns.Value is not string nsValue ||
methodDeclInfo is not IMethodSymbol methodSymbol ||
methodSymbol.Parameters.Length != 2 ||
methodSymbol.Parameters[1].RefKind != RefKind.Out)
{
continue;
}
var parameters = new SeparatedSyntaxList<ParameterSyntax>().AddRange(method.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList<AttributeListSyntax>())));
var methodDecl = method
.WithAttributeLists(new SyntaxList<AttributeListSyntax>())
.WithParameterList(method.ParameterList.WithParameters(parameters))
.WithBody(null)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
.WithoutTrivia().ToString();
return new MethodTarget(methodSymbol, methodDecl, baseType, nsValue);
}
}
return null;
}
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var typesProvider = context.SyntaxProvider.CreateSyntaxProvider(
static (syntaxNode, token) =>
{
token.ThrowIfCancellationRequested();
return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax;
},
static (syntaxContext, token) =>
{
token.ThrowIfCancellationRequested();
return syntaxContext.Node is ClassDeclarationSyntax or StructDeclarationSyntax &&
syntaxContext.SemanticModel.GetDeclaredSymbol(syntaxContext.Node) is ITypeSymbol typeSymbol
? typeSymbol : null;
})
.SelectMany((type, token) =>
{
token.ThrowIfCancellationRequested();
return type is null ? Array.Empty<ITypeSymbol>() : new ITypeSymbol[] { type };
});
var methodsProvider = context.SyntaxProvider.CreateSyntaxProvider(
static (syntaxNode, token) =>
{
token.ThrowIfCancellationRequested();
return syntaxNode is MethodDeclarationSyntax { AttributeLists.Count: > 0 };
}, PopulateMethodTargets)
.SelectMany((method, token) =>
{
token.ThrowIfCancellationRequested();
return method is null ? Array.Empty<MethodTarget>() : new MethodTarget[] { method.Value };
});
var generateContext = methodsProvider.Combine(typesProvider.Collect());
context.RegisterSourceOutput(generateContext, static (sourceContext, source) =>
{
sourceContext.CancellationToken.ThrowIfCancellationRequested();
GenerateSubTypes(sourceContext, source.Left, source.Right);
});
}
}
}

17
src/Shared/SourceGeneratorAttributes.cs

@ -0,0 +1,17 @@
using System;
namespace Avalonia.SourceGenerator
{
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
internal sealed class SubtypesFactoryAttribute : Attribute
{
public SubtypesFactoryAttribute(Type baseType, string @namespace)
{
BaseType = baseType;
Namespace = @namespace;
}
public string Namespace { get; }
public Type BaseType { get; }
}
}
Loading…
Cancel
Save