Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniël te Winkel committed Jul 18, 2023
1 parent 246ee8d commit 84d2089
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 19 deletions.
35 changes: 18 additions & 17 deletions InterfaceStubGenerator.Shared/InterfaceStubGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public void GenerateInterfaceStubs<TContext>(
var methodSymbols = new List<IMethodSymbol>();
foreach (var group in candidateMethods.GroupBy(m => m.SyntaxTree))
{
var model = compilation.GetSemanticModel(group.Key);
var model = compilation.GetSemanticModel(group.Key);
foreach (var method in group)
{
// Get the symbol being declared by the method
Expand Down Expand Up @@ -148,7 +148,7 @@ public void GenerateInterfaceStubs<TContext>(

// Bail out if there aren't any interfaces to generate code for. This may be the case with transitives
if(!interfaces.Any()) return;


var supportsNullable = options.LanguageVersion >= LanguageVersion.CSharp8;

Expand Down Expand Up @@ -222,16 +222,17 @@ internal static partial class Generated
httpMethodBaseAttributeSymbol,
supportsNullable,
interfaceToNullableEnabledMap[group.Key]);

var keyName = group.Key.Name;
if(keyCount.TryGetValue(keyName, out var value))
int value;
while(keyCount.TryGetValue(keyName, out value))
{
keyName = $"{keyName}{++value}";
}
keyCount[keyName] = value;

addSource(context, $"{keyName}.g.cs", SourceText.From(classSource, Encoding.UTF8));
}
}

}

Expand Down Expand Up @@ -315,7 +316,7 @@ partial class {ns}{classDeclaration}
Client = client;
this.requestBuilder = requestBuilder;
}}
");
// Get any other methods on the refit interfaces. We'll need to generate something for them and warn
var nonRefitMethods = interfaceSymbol.GetMembers().OfType<IMethodSymbol>().Except(refitMethods, SymbolEqualityComparer.Default).Cast<IMethodSymbol>().ToList();
Expand All @@ -335,7 +336,7 @@ partial class {ns}{classDeclaration}
var derivedRefitMethods = derivedMethods.Where(m => IsRefitMethod(m, httpMethodBaseAttributeSymbol)).ToList();
var derivedNonRefitMethods = derivedMethods.Except(derivedMethods, SymbolEqualityComparer.Default).Cast<IMethodSymbol>().ToList();

// Handle Refit Methods
// Handle Refit Methods
foreach(var method in refitMethods)
{
ProcessRefitMethod(source, method, true);
Expand Down Expand Up @@ -363,7 +364,7 @@ partial class {ns}{classDeclaration}
if(disposeMethod != null)
{
ProcessDisposableMethod(source, disposeMethod);
}
}

source.Append(@"
}
Expand Down Expand Up @@ -392,21 +393,21 @@ void ProcessRefitMethod(StringBuilder source, IMethodSymbol methodSymbol, bool i
argList.Add($"@{param.MetadataName}");
}

// List of types.
// List of types.
var typeList = new List<string>();
foreach(var param in methodSymbol.Parameters)
{
typeList.Add($"typeof({param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)})");
typeList.Add($"typeof({param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)})");
}

// List of generic arguments
var genericList = new List<string>();
var genericList = new List<string>();
foreach(var typeParam in methodSymbol.TypeParameters)
{
genericList.Add($"typeof({typeParam.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)})");
}

var genericString = genericList.Count > 0 ? $", new global::System.Type[] {{ {string.Join(", ", genericList)} }}" : string.Empty;
var genericString = genericList.Count > 0 ? $", new global::System.Type[] {{ {string.Join(", ", genericList)} }}" : string.Empty;

source.Append(@$"
var ______arguments = new object[] {{ {string.Join(", ", argList)} }};
Expand Down Expand Up @@ -468,7 +469,7 @@ void WriteConstraitsForTypeParameter(StringBuilder source, ITypeParameterSymbol
parameters.Add(typeConstraint.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
}
}

// new constraint has to be last
if (typeParameter.HasConstructorConstraint && !isOverrideOrExplicitImplementation)
{
Expand Down Expand Up @@ -497,7 +498,7 @@ void ProcessNonRefitMethod<TContext>(TContext context, Action<TContext, Diagnost
{
var diagnostic = Diagnostic.Create(InvalidRefitMember, location, methodSymbol.ContainingType.Name, methodSymbol.Name);
reportDiagnostic(context, diagnostic);
}
}
}

void WriteMethodOpening(StringBuilder source, IMethodSymbol methodSymbol, bool isExplicitInterface)
Expand All @@ -513,7 +514,7 @@ void WriteMethodOpening(StringBuilder source, IMethodSymbol methodSymbol, bool i
{
source.Append(@$"{methodSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}.");
}
source.Append(@$"{methodSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}(");
source.Append(@$"{methodSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}(");

if(methodSymbol.Parameters.Length > 0)
{
Expand All @@ -527,7 +528,7 @@ void WriteMethodOpening(StringBuilder source, IMethodSymbol methodSymbol, bool i

source.Append(string.Join(", ", list));
}

source.Append(@$") {GenerateConstraints(methodSymbol.TypeParameters, isExplicitInterface)}
{{");
}
Expand Down Expand Up @@ -595,7 +596,7 @@ class SyntaxReceiver : ISyntaxReceiver

public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
// We're looking for methods with an attribute that are in an interfaces
// We're looking for methods with an attribute that are in an interfaces
if(syntaxNode is MethodDeclarationSyntax methodDeclarationSyntax &&
methodDeclarationSyntax.Parent is InterfaceDeclarationSyntax &&
methodDeclarationSyntax.AttributeLists.Count > 0)
Expand Down
12 changes: 12 additions & 0 deletions Refit.Tests/NamespaceCollisionApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,21 @@ public static INamespaceCollisionApi Create()
namespace CollisionA
{
public class SomeType { }

public interface INamespaceCollisionApi
{
[Get("/")]
Task<SomeType> SomeRequest();
}
}

namespace CollisionB
{
public class SomeType { }

public interface INamespaceCollisionApi
{
[Get("/")]
Task<SomeType> SomeRequest();
}
}
45 changes: 43 additions & 2 deletions Refit.Tests/RestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2120,18 +2120,59 @@ public async Task TypeCollisionTest()
mockHttp.Expect(HttpMethod.Get, Url)
.Respond("application/json", "{ }");

var fixtureA = RestService.For<ITypeCollisionApiA>(Url);
var fixtureA = RestService.For<ITypeCollisionApiA>(Url, settings);

var respA = await fixtureA.SomeARequest();

var fixtureB = RestService.For<ITypeCollisionApiB>(Url);
mockHttp.Expect(HttpMethod.Get, Url)
.Respond("application/json", "{ }");

var fixtureB = RestService.For<ITypeCollisionApiB>(Url, settings);

var respB = await fixtureB.SomeBRequest();

Assert.IsType<CollisionA.SomeType>(respA);
Assert.IsType<CollisionB.SomeType>(respB);
}

[Fact]
public async Task SameTypeNameInMultipleNamespacesTest()
{
var mockHttp = new MockHttpMessageHandler();

var settings = new RefitSettings
{
HttpMessageHandlerFactory = () => mockHttp,
};

const string Url = "https://httpbin.org/get";

mockHttp.Expect(HttpMethod.Get, Url + "/")
.Respond("application/json", "{ }");

var fixtureA = RestService.For<INamespaceCollisionApi>(Url, settings);

var respA = await fixtureA.SomeRequest();

mockHttp.Expect(HttpMethod.Get, Url + "/")
.Respond("application/json", "{ }");

var fixtureB = RestService.For<CollisionA.INamespaceCollisionApi>(Url, settings);

var respB = await fixtureB.SomeRequest();

mockHttp.Expect(HttpMethod.Get, Url + "/")
.Respond("application/json", "{ }");

var fixtureC = RestService.For<CollisionB.INamespaceCollisionApi>(Url, settings);

var respC = await fixtureC.SomeRequest();

Assert.IsType<CollisionA.SomeType>(respA);
Assert.IsType<CollisionA.SomeType>(respB);
Assert.IsType<CollisionB.SomeType>(respC);
}




Expand Down

0 comments on commit 84d2089

Please sign in to comment.