diff --git a/src/Avalonia.Base/Animation/Easings/Easing.cs b/src/Avalonia.Base/Animation/Easings/Easing.cs
index 6dfc4c86b3..d4f817ccc3 100644
--- a/src/Avalonia.Base/Animation/Easings/Easing.cs
+++ b/src/Avalonia.Base/Animation/Easings/Easing.cs
@@ -37,14 +37,9 @@ namespace Avalonia.Animation.Easings
return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture));
}
- if (TryCreateEasingInstance(e, out var easing))
- {
- return easing;
- }
- else
- {
- throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace.");
- }
+ return TryCreateEasingInstance(e, out var easing)
+ ? easing
+ : throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace.");
}
}
}
diff --git a/src/Avalonia.SourceGenerator/IsExternalInit.cs b/src/Avalonia.SourceGenerator/IsExternalInit.cs
new file mode 100644
index 0000000000..c6ddf762ad
--- /dev/null
+++ b/src/Avalonia.SourceGenerator/IsExternalInit.cs
@@ -0,0 +1,14 @@
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Runtime.CompilerServices
+{
+ ///
+ /// Reserved to be used by the compiler for tracking metadata.
+ /// This class should not be used by developers in source code.
+ ///
+ [ExcludeFromCodeCoverage, DebuggerNonUserCode]
+ internal static class IsExternalInit
+ {
+ }
+}
diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs
index 7bc240853e..6ba1f6ac18 100644
--- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs
+++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs
@@ -1,140 +1,48 @@
-using System.Collections.Generic;
+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
{
- internal class GenerateSubtypesSyntaxReceiver : ISyntaxReceiver
+ [Generator(LanguageNames.CSharp)]
+ public class SubtypesFactoryGenerator : IIncrementalGenerator
{
- public List<(MethodDeclarationSyntax, AttributeSyntax)> CandidateMethods { get; } = new();
- public List Types { get; } = new();
-
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
- {
- if (syntaxNode is MethodDeclarationSyntax declarationSyntax)
- {
- foreach (var attribute in declarationSyntax.AttributeLists.SelectMany(i => i.Attributes))
- {
- CandidateMethods.Add((declarationSyntax, attribute));
- }
- }
-
- if (syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax)
- {
- Types.Add(syntaxNode);
- }
- }
- }
-
- [Generator]
- internal class SubtypesFactoryGenerator : ISourceGenerator
- {
- private readonly GenerateSubtypesSyntaxReceiver _receiver = new();
+ private record MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace);
private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName;
- public void Execute(GeneratorExecutionContext context)
+ private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType)
{
- var methods = new List<(IMethodSymbol, ITypeSymbol, string)>();
-
- foreach (var (method, attribute) in _receiver.CandidateMethods)
- {
- var semanticModel = context.Compilation.GetSemanticModel(method.SyntaxTree);
- 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;
- }
-
- methods.Add((methodSymbol, baseType, nsValue));
- }
-
- var types = new List();
- foreach (var type in _receiver.Types)
- {
- var semanticModel = context.Compilation.GetSemanticModel(type.SyntaxTree);
- var decl = semanticModel.GetDeclaredSymbol(type);
- if (decl is ITypeSymbol typeSymbol)
- {
- types.Add(typeSymbol);
- }
- }
-
- GenerateSubTypes(context, methods, types);
+ return type.BaseType is not null && (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType) || IsSubtypeOf(type.BaseType, baseType));
}
- private bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType)
+ private static void GenerateSubTypes(SourceProductionContext context, MethodTarget methodTarget, ImmutableArray types)
{
- if (type.BaseType is null)
- {
- return false;
- }
+ var (method, 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;
- if (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType))
+ if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl)
{
- return true;
+ return;
}
- return IsSubtypeOf(type.BaseType, baseType);
- }
+ var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList())));
- private void GenerateSubTypes(
- GeneratorExecutionContext context,
- List<(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace)> methods,
- List types)
- {
- foreach (var (method, baseType, @namespace) in methods)
- {
- 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;
-
- if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl)
- {
- continue;
- }
+ var methodDeclText = methodDecl
+ .WithAttributeLists(new SyntaxList())
+ .WithParameterList(methodDecl.ParameterList.WithParameters(parameters))
+ .WithBody(null)
+ .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
+ .WithoutTrivia().ToString();
- var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList())));
-
- var methodDeclText = methodDecl
- .WithAttributeLists(new SyntaxList())
- .WithParameterList(methodDecl.ParameterList.WithParameters(parameters))
- .WithBody(null)
- .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
- .WithoutTrivia().ToString();
-
- var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}";
- var source = $@"using System;
+ var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}";
+ var source = $@"using System;
using System.Collections.Generic;
namespace {method.ContainingNamespace}
@@ -155,13 +63,99 @@ namespace {method.ContainingNamespace}
}}
}}";
- context.AddSource($"{type}.{method.MetadataName}.gen.cs", source);
+ 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;
+ }
+
+ return new MethodTarget(methodSymbol, baseType, nsValue);
+ }
}
+
+ return null;
}
- public void Initialize(GeneratorInitializationContext context)
+ public void Initialize(IncrementalGeneratorInitializationContext context)
{
- context.RegisterForSyntaxNotifications(() => _receiver);
+ 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() : new ITypeSymbol[] { type };
+ });
+
+ var methodsProvider = context.SyntaxProvider.CreateSyntaxProvider(
+ static (syntaxNode, token) =>
+ {
+ token.ThrowIfCancellationRequested();
+ return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax or MethodDeclarationSyntax { AttributeLists.Count: > 0 };
+ }, PopulateMethodTargets)
+ .SelectMany((method, token) =>
+ {
+ token.ThrowIfCancellationRequested();
+ return method is null ? Array.Empty() : new MethodTarget[] { method };
+ });
+
+ var generateContext = methodsProvider.Combine(typesProvider.Collect());
+
+ context.RegisterSourceOutput(generateContext, static (sourceContext, source) =>
+ {
+ sourceContext.CancellationToken.ThrowIfCancellationRequested();
+ GenerateSubTypes(sourceContext, source.Left, source.Right);
+ });
}
}
}