From ca28202a65b22b6fd3ea66b4c75eab252255a097 Mon Sep 17 00:00:00 2001 From: neuecc Date: Fri, 20 Sep 2024 18:30:39 +0900 Subject: [PATCH] TODO: impl true incremental generator --- src/MemoryPack.Generator/EquatableArray.cs | 58 +++++++++++++++++++ .../EquatableTypeSymbol.cs | 33 +++++++++++ src/MemoryPack.Generator/Extensions.cs | 23 ++++++++ .../MemoryPack.Generator.csproj | 2 +- .../MemoryPackGenerator.cs | 33 ++++++----- .../GeneratorDiagnosticsTest.ManyMembers.cs | 0 .../GeneratorDiagnosticsTest.TypeScript.cs | 0 .../GeneratorDiagnosticsTest.cs | 0 .../IncrementalGeneratorTest.cs | 38 ++++++++++++ 9 files changed, 173 insertions(+), 14 deletions(-) create mode 100644 src/MemoryPack.Generator/EquatableArray.cs create mode 100644 src/MemoryPack.Generator/EquatableTypeSymbol.cs rename tests/MemoryPack.Tests/{ => SourceGeneratorTests}/GeneratorDiagnosticsTest.ManyMembers.cs (100%) rename tests/MemoryPack.Tests/{ => SourceGeneratorTests}/GeneratorDiagnosticsTest.TypeScript.cs (100%) rename tests/MemoryPack.Tests/{ => SourceGeneratorTests}/GeneratorDiagnosticsTest.cs (100%) create mode 100644 tests/MemoryPack.Tests/SourceGeneratorTests/IncrementalGeneratorTest.cs diff --git a/src/MemoryPack.Generator/EquatableArray.cs b/src/MemoryPack.Generator/EquatableArray.cs new file mode 100644 index 00000000..62b61536 --- /dev/null +++ b/src/MemoryPack.Generator/EquatableArray.cs @@ -0,0 +1,58 @@ +using System.Collections; +using System.Runtime.CompilerServices; + +namespace MemoryPack.Generator; + +public readonly struct EquatableArray : IEquatable>, IEnumerable + where T : IEquatable +{ + readonly T[]? array; + + public EquatableArray() // for collection literal [] + { + array = []; + } + + public EquatableArray(T[] array) + { + this.array = array; + } + + public static implicit operator EquatableArray(T[] array) + { + return new EquatableArray(array); + } + + public ref readonly T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref array![index]; + } + + public int Length => array!.Length; + + public ReadOnlySpan AsSpan() + { + return array.AsSpan(); + } + + public ReadOnlySpan.Enumerator GetEnumerator() + { + return AsSpan().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return array.AsEnumerable().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return array.AsEnumerable().GetEnumerator(); + } + + public bool Equals(EquatableArray other) + { + return AsSpan().SequenceEqual(other.AsSpan()); + } +} diff --git a/src/MemoryPack.Generator/EquatableTypeSymbol.cs b/src/MemoryPack.Generator/EquatableTypeSymbol.cs new file mode 100644 index 00000000..a952df85 --- /dev/null +++ b/src/MemoryPack.Generator/EquatableTypeSymbol.cs @@ -0,0 +1,33 @@ +using Microsoft.CodeAnalysis; +using System.Collections.Immutable; + +namespace MemoryPack.Generator; + +public class EquatableTypeSymbol(ITypeSymbol typeSymbol) : IEquatable +{ + // Used for build argument parser, maybe ok to equals name. + public ITypeSymbol TypeSymbol => typeSymbol; + + // GetMembers is called for Enum and fields is not condition for command equality. + public ImmutableArray GetMembers() => typeSymbol.GetMembers(); + + public TypeKind TypeKind { get; } = typeSymbol.TypeKind; + public SpecialType SpecialType { get; } = typeSymbol.SpecialType; + + public string ToFullyQualifiedFormatDisplayString() => typeSymbol.ToFullyQualifiedFormatDisplayString(); + public string ToDisplayString(NullableFlowState state, SymbolDisplayFormat format) => typeSymbol.ToDisplayString(state, format); + + public bool Equals(EquatableTypeSymbol other) + { + if (this.TypeKind != other.TypeKind) return false; + if (this.SpecialType != other.SpecialType) return false; + if (this.TypeSymbol.Name != other.TypeSymbol.Name) return false; + + return this.TypeSymbol.EqualsNamespaceAndName(other.TypeSymbol); + } +} + +static class EquatableTypeSymbolExtensions +{ + public static EquatableTypeSymbol ToEquatable(this ITypeSymbol typeSymbol) => new(typeSymbol); +} diff --git a/src/MemoryPack.Generator/Extensions.cs b/src/MemoryPack.Generator/Extensions.cs index cbf0b3d8..c4a2de13 100644 --- a/src/MemoryPack.Generator/Extensions.cs +++ b/src/MemoryPack.Generator/Extensions.cs @@ -12,6 +12,24 @@ public static string NewLine(this IEnumerable source) return string.Join(Environment.NewLine, source); } + public static bool EqualsNamespaceAndName(this ITypeSymbol? left, ITypeSymbol? right) + { + if (left == null && right == null) return true; + if (left == null || right == null) return false; + + var l = left.ContainingNamespace; + var r = right.ContainingNamespace; + while (l != null && r != null) + { + if (l.Name != r.Name) return false; + + l = l.ContainingNamespace; + r = r.ContainingNamespace; + } + + return (left.Name == right.Name); + } + public static bool ContainsAttribute(this ISymbol symbol, INamedTypeSymbol attribtue) { return symbol.GetAttributes().Any(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, attribtue)); @@ -213,6 +231,11 @@ public static IEnumerable GetAllBaseTypes(this INamedTypeSymbo } } + internal static string ToFullyQualifiedFormatDisplayString(this ITypeSymbol typeSymbol) + { + return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + } + public static string FullyQualifiedToString(this ISymbol symbol) { return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); diff --git a/src/MemoryPack.Generator/MemoryPack.Generator.csproj b/src/MemoryPack.Generator/MemoryPack.Generator.csproj index 2e931807..d6fe8a2c 100644 --- a/src/MemoryPack.Generator/MemoryPack.Generator.csproj +++ b/src/MemoryPack.Generator/MemoryPack.Generator.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 11 + 12 enable cs enable diff --git a/src/MemoryPack.Generator/MemoryPackGenerator.cs b/src/MemoryPack.Generator/MemoryPackGenerator.cs index f747128c..760a4c0a 100644 --- a/src/MemoryPack.Generator/MemoryPackGenerator.cs +++ b/src/MemoryPack.Generator/MemoryPackGenerator.cs @@ -55,7 +55,18 @@ void RegisterMemoryPackable(IncrementalGeneratorInitializationContext context) } return (string?)null; - }); + }) + .WithTrackingName("MemoryPack.MemoryPackable.0_AnalyzerConfigOptionsProvider"); // annotate for IncrementalGeneratorTest + + var parseOptions = context.ParseOptionsProvider + .Select((parseOptions, token) => + { + var csOptions = (CSharpParseOptions)parseOptions; + var langVersion = csOptions.LanguageVersion; + var net7 = csOptions.PreprocessorSymbolNames.Contains("NET7_0_OR_GREATER"); + return (langVersion, net7); + }) + .WithTrackingName("MemoryPack.MemoryPackable.0_ParseOptionsProvider"); var typeDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName( MemoryPackableAttributeFullName, @@ -70,7 +81,8 @@ or RecordDeclarationSyntax transform: static (context, token) => { return (TypeDeclarationSyntax)context.TargetNode; - }); + }) + .WithTrackingName("MemoryPack.MemoryPackable.1_ForAttributeMemoryPackableAttribute"); var typeDeclarations2 = context.SyntaxProvider.ForAttributeWithMetadataName( MemoryPackUnionFormatterAttributeFullName, @@ -81,22 +93,16 @@ or RecordDeclarationSyntax transform: static (context, token) => { return (TypeDeclarationSyntax)context.TargetNode; - }); - - var parseOptions = context.ParseOptionsProvider.Select((parseOptions, token) => - { - var csOptions = (CSharpParseOptions)parseOptions; - var langVersion = csOptions.LanguageVersion; - var net7 = csOptions.PreprocessorSymbolNames.Contains("NET7_0_OR_GREATER"); - return (langVersion, net7); - }); + }) + .WithTrackingName("MemoryPack.MemoryPackable.1_ForAttributeMemoryPackUnion"); { var source = typeDeclarations .Combine(context.CompilationProvider) .WithComparer(Comparer.Instance) .Combine(logProvider) - .Combine(parseOptions); + .Combine(parseOptions) + .WithTrackingName("MemoryPack.MemoryPackable.2_MemoryPackableCombined"); context.RegisterSourceOutput(source, static (context, source) => { @@ -112,7 +118,8 @@ or RecordDeclarationSyntax .Combine(context.CompilationProvider) .WithComparer(Comparer.Instance) .Combine(logProvider) - .Combine(parseOptions); + .Combine(parseOptions) + .WithTrackingName("MemoryPack.MemoryPackable.2_MemoryPackUnionCombined"); context.RegisterSourceOutput(source, static (context, source) => { diff --git a/tests/MemoryPack.Tests/GeneratorDiagnosticsTest.ManyMembers.cs b/tests/MemoryPack.Tests/SourceGeneratorTests/GeneratorDiagnosticsTest.ManyMembers.cs similarity index 100% rename from tests/MemoryPack.Tests/GeneratorDiagnosticsTest.ManyMembers.cs rename to tests/MemoryPack.Tests/SourceGeneratorTests/GeneratorDiagnosticsTest.ManyMembers.cs diff --git a/tests/MemoryPack.Tests/GeneratorDiagnosticsTest.TypeScript.cs b/tests/MemoryPack.Tests/SourceGeneratorTests/GeneratorDiagnosticsTest.TypeScript.cs similarity index 100% rename from tests/MemoryPack.Tests/GeneratorDiagnosticsTest.TypeScript.cs rename to tests/MemoryPack.Tests/SourceGeneratorTests/GeneratorDiagnosticsTest.TypeScript.cs diff --git a/tests/MemoryPack.Tests/GeneratorDiagnosticsTest.cs b/tests/MemoryPack.Tests/SourceGeneratorTests/GeneratorDiagnosticsTest.cs similarity index 100% rename from tests/MemoryPack.Tests/GeneratorDiagnosticsTest.cs rename to tests/MemoryPack.Tests/SourceGeneratorTests/GeneratorDiagnosticsTest.cs diff --git a/tests/MemoryPack.Tests/SourceGeneratorTests/IncrementalGeneratorTest.cs b/tests/MemoryPack.Tests/SourceGeneratorTests/IncrementalGeneratorTest.cs new file mode 100644 index 00000000..87f6a9eb --- /dev/null +++ b/tests/MemoryPack.Tests/SourceGeneratorTests/IncrementalGeneratorTest.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MemoryPack.Tests.SourceGeneratorTests; + +public class IncrementalGeneratorTest +{ + [Fact] + public void Run() + { + // lang=C#-test + var step1 = """ +[MemoryPackable] +public partial class MyClass +{ + public int MyProperty { get; set; } +} +"""; + + // lang=C#-test + var step2 = """ +[MemoryPackable] +public partial class MyClass +{ + public int MyProperty { get; set; } + // unrelated line +} +"""; + + var hoge = CSharpGeneratorRunner.GetIncrementalGeneratorTrackedStepsReasons("MemoryPack.MemoryPackable.", step1, step2); + + } +} + +