Skip to content

Commit

Permalink
feat: add UniqueNameBuilder (#1894)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimothyMakkison authored Oct 28, 2024
1 parent 6de1dbb commit 0ba7394
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 22 deletions.
31 changes: 9 additions & 22 deletions InterfaceStubGenerator.Shared/Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,18 @@ partial class {model.Ns}{model.ClassDeclaration}
"
);

var memberNames = new HashSet<string>(model.MemberNames);
var uniqueNames = new UniqueNameBuilder();
uniqueNames.Reserve(model.MemberNames);

// Handle Refit Methods
foreach (var method in model.RefitMethods)
{
WriteRefitMethod(source, method, true, memberNames);
WriteRefitMethod(source, method, true, uniqueNames);
}

foreach (var method in model.DerivedRefitMethods)
{
WriteRefitMethod(source, method, false, memberNames);
WriteRefitMethod(source, method, false, uniqueNames);
}

// Handle non-refit Methods that aren't static or properties or have a method body
Expand Down Expand Up @@ -155,18 +156,18 @@ partial class {model.Ns}{model.ClassDeclaration}
/// <param name="source"></param>
/// <param name="methodModel"></param>
/// <param name="isTopLevel">True if directly from the type we're generating for, false for methods found on base interfaces</param>
/// <param name="memberNames">Contains the unique member names in the interface scope.</param>
/// <param name="uniqueNames">Contains the unique member names in the interface scope.</param>
private static void WriteRefitMethod(
StringBuilder source,
MethodModel methodModel,
bool isTopLevel,
HashSet<string> memberNames
UniqueNameBuilder uniqueNames
)
{
var parameterTypesExpression = GenerateTypeParameterExpression(
source,
methodModel,
memberNames
uniqueNames
);

var returnType = methodModel.ReturnType;
Expand Down Expand Up @@ -253,7 +254,7 @@ private static void WriteDisposableMethod(StringBuilder source)
private static string GenerateTypeParameterExpression(
StringBuilder source,
MethodModel methodModel,
HashSet<string> memberNames
UniqueNameBuilder uniqueNames
)
{
// use Array.Empty if method has no parameters.
Expand All @@ -268,7 +269,7 @@ HashSet<string> memberNames
}

// find a name and generate field declaration.
var typeParameterFieldName = UniqueName(TypeParameterVariableName, memberNames);
var typeParameterFieldName = uniqueNames.New(TypeParameterVariableName);
var types = string.Join(", ", methodModel.Parameters.Select(x => $"typeof({x.Type})"));
source.Append(
$$"""
Expand Down Expand Up @@ -325,20 +326,6 @@ private static void WriteMethodOpening(

private static void WriteMethodClosing(StringBuilder source) => source.Append(@" }");

private static string UniqueName(string name, HashSet<string> methodNames)
{
var candidateName = name;
var counter = 0;
while (methodNames.Contains(candidateName))
{
candidateName = $"{name}{counter}";
counter++;
}

methodNames.Add(candidateName);
return candidateName;
}

private static string GenerateConstraints(
ImmutableEquatableArray<TypeConstraint> typeParameters,
bool isOverrideOrExplicitImplementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Models\ParameterModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\TypeConstraint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UniqueNameBuilder.cs" />
</ItemGroup>
</Project>
51 changes: 51 additions & 0 deletions InterfaceStubGenerator.Shared/UniqueNameBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace Refit.Generator;

public class UniqueNameBuilder()

Check warning on line 3 in InterfaceStubGenerator.Shared/UniqueNameBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'UniqueNameBuilder'

Check warning on line 3 in InterfaceStubGenerator.Shared/UniqueNameBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'UniqueNameBuilder.UniqueNameBuilder()'
{
private readonly HashSet<string> _usedNames = new(StringComparer.Ordinal);
private readonly UniqueNameBuilder? _parentScope;

private UniqueNameBuilder(UniqueNameBuilder parentScope)
: this()
{
_parentScope = parentScope;
}

public void Reserve(string name) => _usedNames.Add(name);

Check warning on line 14 in InterfaceStubGenerator.Shared/UniqueNameBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'UniqueNameBuilder.Reserve(string)'

public UniqueNameBuilder NewScope() => new(this);

Check warning on line 16 in InterfaceStubGenerator.Shared/UniqueNameBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'UniqueNameBuilder.NewScope()'

public string New(string name)

Check warning on line 18 in InterfaceStubGenerator.Shared/UniqueNameBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'UniqueNameBuilder.New(string)'
{
var i = 0;
var uniqueName = name;
while (Contains(uniqueName))
{
uniqueName = name + i;
i++;
}

_usedNames.Add(uniqueName);

return uniqueName;
}

public void Reserve(IEnumerable<string> names)

Check warning on line 33 in InterfaceStubGenerator.Shared/UniqueNameBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'UniqueNameBuilder.Reserve(IEnumerable<string>)'
{
foreach (var name in names)

Check warning on line 35 in InterfaceStubGenerator.Shared/UniqueNameBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build

In externally visible method 'void UniqueNameBuilder.Reserve(IEnumerable<string> names)', validate parameter 'names' is non-null before using it. If appropriate, throw an 'ArgumentNullException' when the argument is 'null'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1062)
{
_usedNames.Add(name);
}
}

private bool Contains(string name)
{
if (_usedNames.Contains(name))
return true;

if (_parentScope != null)
return _parentScope.Contains(name);

return false;
}
}

0 comments on commit 0ba7394

Please sign in to comment.