diff --git a/InterfaceStubGenerator.Shared/Emitter.cs b/InterfaceStubGenerator.Shared/Emitter.cs index 88b49e5fe..a1305fb48 100644 --- a/InterfaceStubGenerator.Shared/Emitter.cs +++ b/InterfaceStubGenerator.Shared/Emitter.cs @@ -112,17 +112,18 @@ partial class {model.Ns}{model.ClassDeclaration} " ); - var memberNames = new HashSet(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 @@ -155,18 +156,18 @@ partial class {model.Ns}{model.ClassDeclaration} /// /// /// True if directly from the type we're generating for, false for methods found on base interfaces - /// Contains the unique member names in the interface scope. + /// Contains the unique member names in the interface scope. private static void WriteRefitMethod( StringBuilder source, MethodModel methodModel, bool isTopLevel, - HashSet memberNames + UniqueNameBuilder uniqueNames ) { var parameterTypesExpression = GenerateTypeParameterExpression( source, methodModel, - memberNames + uniqueNames ); var returnType = methodModel.ReturnType; @@ -253,7 +254,7 @@ private static void WriteDisposableMethod(StringBuilder source) private static string GenerateTypeParameterExpression( StringBuilder source, MethodModel methodModel, - HashSet memberNames + UniqueNameBuilder uniqueNames ) { // use Array.Empty if method has no parameters. @@ -268,7 +269,7 @@ HashSet 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( $$""" @@ -325,20 +326,6 @@ private static void WriteMethodOpening( private static void WriteMethodClosing(StringBuilder source) => source.Append(@" }"); - private static string UniqueName(string name, HashSet 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 typeParameters, bool isOverrideOrExplicitImplementation diff --git a/InterfaceStubGenerator.Shared/InterfaceStubGenerator.Shared.projitems b/InterfaceStubGenerator.Shared/InterfaceStubGenerator.Shared.projitems index ea53907fe..8eab43eb8 100644 --- a/InterfaceStubGenerator.Shared/InterfaceStubGenerator.Shared.projitems +++ b/InterfaceStubGenerator.Shared/InterfaceStubGenerator.Shared.projitems @@ -22,5 +22,6 @@ + \ No newline at end of file diff --git a/InterfaceStubGenerator.Shared/UniqueNameBuilder.cs b/InterfaceStubGenerator.Shared/UniqueNameBuilder.cs new file mode 100644 index 000000000..feed8ecde --- /dev/null +++ b/InterfaceStubGenerator.Shared/UniqueNameBuilder.cs @@ -0,0 +1,51 @@ +namespace Refit.Generator; + +public class UniqueNameBuilder() +{ + private readonly HashSet _usedNames = new(StringComparer.Ordinal); + private readonly UniqueNameBuilder? _parentScope; + + private UniqueNameBuilder(UniqueNameBuilder parentScope) + : this() + { + _parentScope = parentScope; + } + + public void Reserve(string name) => _usedNames.Add(name); + + public UniqueNameBuilder NewScope() => new(this); + + public string New(string name) + { + var i = 0; + var uniqueName = name; + while (Contains(uniqueName)) + { + uniqueName = name + i; + i++; + } + + _usedNames.Add(uniqueName); + + return uniqueName; + } + + public void Reserve(IEnumerable names) + { + foreach (var name in names) + { + _usedNames.Add(name); + } + } + + private bool Contains(string name) + { + if (_usedNames.Contains(name)) + return true; + + if (_parentScope != null) + return _parentScope.Contains(name); + + return false; + } +}