Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update InterfaceStubGenerator to be an incremental generator #1864

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading