Skip to content

Commit

Permalink
feat: add incremental generator (#1864)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimothyMakkison authored Oct 10, 2024
1 parent 48d1256 commit 26cfb28
Show file tree
Hide file tree
Showing 18 changed files with 1,314 additions and 744 deletions.
414 changes: 414 additions & 0 deletions InterfaceStubGenerator.Shared/Emitter.cs

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions InterfaceStubGenerator.Shared/ImmutableEquatableArray.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System.Collections;

namespace Refit.Generator;

internal static class ImmutableEquatableArray
{
public static ImmutableEquatableArray<T> Empty<T>()
where T : IEquatable<T> => ImmutableEquatableArray<T>.Empty;

public static ImmutableEquatableArray<T> ToImmutableEquatableArray<T>(
this IEnumerable<T>? values
)
where T : IEquatable<T> => values == null ? Empty<T>() : new(values);
}

/// <summary>
/// Provides an immutable list implementation which implements sequence equality.
/// </summary>
internal sealed class ImmutableEquatableArray<T>
: IEquatable<ImmutableEquatableArray<T>>,
IReadOnlyList<T>
where T : IEquatable<T>
{
public static ImmutableEquatableArray<T> Empty { get; } = new(Array.Empty<T>());

private readonly T[] _values;
public T this[int index] => _values[index];
public int Count => _values.Length;

public ImmutableEquatableArray(T[] values) => _values = values;

public ImmutableEquatableArray(IEnumerable<T> values) => _values = values.ToArray();

public T[] AsArray() => _values;

public bool Equals(ImmutableEquatableArray<T>? other) =>
other != null && ((ReadOnlySpan<T>)_values).SequenceEqual(other._values);

public override bool Equals(object? obj) =>
obj is ImmutableEquatableArray<T> other && Equals(other);

public override int GetHashCode()
{
var hash = 0;
foreach (T value in _values)
{
hash = Combine(hash, value.GetHashCode());
}

static int Combine(int h1, int h2)
{
// RyuJIT optimizes this to use the ROL instruction
// Related GitHub pull request: https://github.com/dotnet/coreclr/pull/1830
uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27);
return ((int)rol5 + h1) ^ h2;
}

return hash;
}

public Enumerator GetEnumerator() => new(_values);

IEnumerator<T> IEnumerable<T>.GetEnumerator() => ((IEnumerable<T>)_values).GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => _values.GetEnumerator();

public struct Enumerator
{
private readonly T[] _values;
private int _index;

internal Enumerator(T[] values)
{
_values = values;
_index = -1;
}

public bool MoveNext()
{
var newIndex = _index + 1;

if ((uint)newIndex < (uint)_values.Length)
{
_index = newIndex;
return true;
}

return false;
}

public readonly T Current => _values[_index];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#if ROSLYN_4
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

namespace Refit.Generator;

internal static class IncrementalValuesProviderExtensions
{
/// <summary>
/// Registers an output node into an <see cref="IncrementalGeneratorInitializationContext"/> to output a diagnostic.
/// </summary>
/// <param name="context">The input <see cref="IncrementalGeneratorInitializationContext"/> instance.</param>
/// <param name="diagnostic">The input <see cref="IncrementalValuesProvider{TValues}"/> sequence of diagnostics.</param>
public static void ReportDiagnostics(
this IncrementalGeneratorInitializationContext context,
IncrementalValuesProvider<Diagnostic> diagnostic
)
{
context.RegisterSourceOutput(
diagnostic,
static (context, diagnostic) => context.ReportDiagnostic(diagnostic)
);
}

/// <summary>
/// Registers an output node into an <see cref="IncrementalGeneratorInitializationContext"/> to output diagnostics.
/// </summary>
/// <param name="context">The input <see cref="IncrementalGeneratorInitializationContext"/> instance.</param>
/// <param name="diagnostics">The input <see cref="IncrementalValuesProvider{TValues}"/> sequence of diagnostics.</param>
public static void ReportDiagnostics(
this IncrementalGeneratorInitializationContext context,
IncrementalValueProvider<ImmutableEquatableArray<Diagnostic>> diagnostics
)
{
context.RegisterSourceOutput(
diagnostics,
static (context, diagnostics) =>
{
foreach (var diagnostic in diagnostics)
{
context.ReportDiagnostic(diagnostic);
}
}
);
}

/// <summary>
/// Registers an implementation source output for the provided mappers.
/// </summary>
/// <param name="context">The context, on which the output is registered.</param>
/// <param name="model">The interfaces stubs.</param>
public static void EmitSource(
this IncrementalGeneratorInitializationContext context,
IncrementalValuesProvider<InterfaceModel> model
)
{
context.RegisterImplementationSourceOutput(
model,
static (spc, model) =>
{
var mapperText = Emitter.EmitInterface(model);
spc.AddSource(model.FileName, SourceText.From(mapperText, Encoding.UTF8));
}
);
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,17 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)DiagnosticDescriptors.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Emitter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ImmutableEquatableArray.cs" />
<Compile Include="$(MSBuildThisFileDirectory)IncrementalValuesProviderExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)InterfaceStubGenerator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)IsExternalInit.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ITypeSymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ContextGenerationModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\InterfaceModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\MethodModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ParameterModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\TypeConstraint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parser.cs" />
</ItemGroup>
</Project>
Loading

0 comments on commit 26cfb28

Please sign in to comment.