Skip to content

Commit

Permalink
TODO: impl true incremental generator
Browse files Browse the repository at this point in the history
  • Loading branch information
neuecc committed Sep 20, 2024
1 parent 9cff3eb commit ca28202
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 14 deletions.
58 changes: 58 additions & 0 deletions src/MemoryPack.Generator/EquatableArray.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Collections;
using System.Runtime.CompilerServices;

namespace MemoryPack.Generator;

public readonly struct EquatableArray<T> : IEquatable<EquatableArray<T>>, IEnumerable<T>
where T : IEquatable<T>
{
readonly T[]? array;

public EquatableArray() // for collection literal []
{
array = [];
}

public EquatableArray(T[] array)
{
this.array = array;
}

public static implicit operator EquatableArray<T>(T[] array)
{
return new EquatableArray<T>(array);
}

public ref readonly T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array![index];
}

public int Length => array!.Length;

public ReadOnlySpan<T> AsSpan()
{
return array.AsSpan();
}

public ReadOnlySpan<T>.Enumerator GetEnumerator()
{
return AsSpan().GetEnumerator();
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return array.AsEnumerable().GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return array.AsEnumerable().GetEnumerator();
}

public bool Equals(EquatableArray<T> other)
{
return AsSpan().SequenceEqual(other.AsSpan());
}
}
33 changes: 33 additions & 0 deletions src/MemoryPack.Generator/EquatableTypeSymbol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;

namespace MemoryPack.Generator;

public class EquatableTypeSymbol(ITypeSymbol typeSymbol) : IEquatable<EquatableTypeSymbol>
{
// 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<ISymbol> 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);
}
23 changes: 23 additions & 0 deletions src/MemoryPack.Generator/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ public static string NewLine(this IEnumerable<string> 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));
Expand Down Expand Up @@ -213,6 +231,11 @@ public static IEnumerable<INamedTypeSymbol> 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);
Expand Down
2 changes: 1 addition & 1 deletion src/MemoryPack.Generator/MemoryPack.Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>11</LangVersion>
<LangVersion>12</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<AnalyzerLanguage>cs</AnalyzerLanguage>
<Nullable>enable</Nullable>
Expand Down
33 changes: 20 additions & 13 deletions src/MemoryPack.Generator/MemoryPackGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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) =>
{
Expand All @@ -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) =>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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);

}
}


0 comments on commit ca28202

Please sign in to comment.