Skip to content

Commit 1631966

Browse files
committed
Started work on generic parameters. Still a work in progress
1 parent a4bd1f2 commit 1631966

11 files changed

+299
-6
lines changed

src/PublicInterfaceGenerator/GeneratorParsers/MethodParser.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,26 @@ public static class MethodParser
3434
returnType = symbol.ReturnType.ToString();
3535
}
3636

37-
var argumentBuilder = ImmutableArray.CreateBuilder<InterfaceToGenerateInfo.Argument>();
37+
var genericParameters = symbol.TypeParameters
38+
.Select(x =>
39+
{
40+
var constraintTypes = string.Join(", ", x.ConstraintTypes.Select(x => x.Name));
41+
return new InterfaceToGenerateInfo.MethodGenericParameter(x.Ordinal, x.Name, x.NullableAnnotation, constraintTypes);
42+
}).ToImmutableArray();
43+
44+
var argumentBuilder = ImmutableArray.CreateBuilder<InterfaceToGenerateInfo.MethodArgument>();
3845

3946
foreach (var methodParameter in symbol.Parameters)
4047
{
4148
var argName = methodParameter.Name;
4249
var dataType = methodParameter.Type.ToDisplayString();
4350
var nullableAnnotation = methodParameter.NullableAnnotation;
4451

45-
var interfaceArgument = new InterfaceToGenerateInfo.Argument(argName, dataType, nullableAnnotation);
52+
var interfaceArgument = new InterfaceToGenerateInfo.MethodArgument(argName, dataType, nullableAnnotation);
4653
argumentBuilder.Add(interfaceArgument);
4754
}
4855

49-
return new InterfaceToGenerateInfo.Method(methodName, returnType, argumentBuilder.ToImmutableArray(), methodComments);
56+
return new InterfaceToGenerateInfo.Method(methodName, returnType, argumentBuilder.ToImmutableArray(), genericParameters, methodComments);
5057
}
5158

5259
private static bool IsSymbolValid(IMethodSymbol symbol, string extraClassInterfaces, bool inheritsFromIDisposable)

src/PublicInterfaceGenerator/InterfaceToGenerateInfo.cs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,70 @@ internal static string CombineLineWithComments(string comments, string definitio
3131
}
3232
}
3333

34-
public record Method(string Name, string ReturnType, ImmutableArray<Argument> Arguments, string Comments)
34+
public record Method(string Name, string ReturnType, ImmutableArray<MethodArgument> Arguments, ImmutableArray<MethodGenericParameter> MethodGenericParameters, string Comments)
3535
{
3636
public string ToMethodString()
3737
{
38-
var definitionLine = $"{ReturnType} {Name}({string.Join(", ", Arguments.Select(a => a.ToArgumentString()))});";
38+
var builder = new StringBuilder();
39+
40+
_ = builder.Append($"{ReturnType} {Name}");
41+
42+
if (MethodGenericParameters.Any())
43+
{
44+
_ = builder.Append('<');
45+
46+
for (int i = 0; i < MethodGenericParameters.Length; i++)
47+
{
48+
var parameter = MethodGenericParameters[i];
49+
_ = builder.Append(parameter.Name);
50+
if (parameter.NullableAnnotation == NullableAnnotation.Annotated)
51+
{
52+
_ = builder.Append('?');
53+
}
54+
55+
//If there are more generic parameters, add a comma to separate items in the list
56+
if (i + 1 < MethodGenericParameters.Length)
57+
{
58+
_ = builder.Append($", ");
59+
}
60+
}
61+
62+
_ = builder.Append('>');
63+
}
64+
65+
_ = builder.Append($"({string.Join(", ", Arguments.Select(a => a.ToArgumentString()))})");
66+
67+
foreach (var genericParam in MethodGenericParameters)
68+
{
69+
if (!string.IsNullOrWhiteSpace(genericParam.ConstraintTypes))
70+
{
71+
_ = builder.Append($" where {genericParam.Name} : {genericParam.ConstraintTypes}");
72+
}
73+
}
74+
75+
_ = builder.Append(';');
76+
77+
var definitionLine = builder.ToString();
3978
return InterfaceToGenerateInfo.CombineLineWithComments(Comments, definitionLine);
4079
}
4180
}
4281

43-
public record Argument(string Name, string DataType, NullableAnnotation NullableAnnotation)
82+
public record MethodArgument(string Name, string DataType, NullableAnnotation NullableAnnotation)
4483
{
4584
public string ToArgumentString()
4685
{
4786
return $"{DataType} {Name}";
4887
}
4988
}
5089

90+
public record MethodGenericParameter(int Ordinal, string Name, NullableAnnotation NullableAnnotation, string ConstraintTypes)
91+
{
92+
public string ToTypeParameter()
93+
{
94+
return $"";
95+
}
96+
}
97+
5198
public record Property(string Name, string ReturnType, bool HasValidGet, bool HasValidSet, string Comments)
5299
{
53100
public string ToPropertyString()

src/UnitTests/MethodsTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#pragma warning disable IDE0058 // Expression value is never used
22

3+
using System.Net.Http.Headers;
4+
5+
using Microsoft.CodeAnalysis.CSharp.Syntax;
6+
37
namespace UnitTests;
48

59
public class MethodsTests
@@ -211,5 +215,91 @@ public void Dispose(){}
211215

212216
await TestHelper.VerifyAsync(source, SnapshotsDirectory);
213217
}
218+
219+
[Fact]
220+
public async Task WhenMethodHasGenericTypes_AssertGenericTypesInInterface()
221+
{
222+
var source = """
223+
using ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.Attributes;
224+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.UnitTestClasses;
225+
226+
[GenerateInterfaceAttribute]
227+
public class MyClass : IMyClass
228+
{
229+
public void GenerateString1<T>(T arg1, T? arg2)
230+
=> Console.WriteLine($"{arg1} {arg2}");
231+
232+
public T GenerateString2<T>(T arg1, T? arg2)
233+
=> arg1;
234+
235+
public T? GenerateString3<T>(T arg1, T? arg2)
236+
=> arg2;
237+
}
238+
""";
239+
240+
await TestHelper.VerifyAsync(source, SnapshotsDirectory);
241+
}
242+
243+
[Fact]
244+
public async Task WhenMethodHasMultipleGenericTypes_AssertGenericTypesInInterface()
245+
{
246+
var source = """
247+
using ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.Attributes;
248+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.UnitTestClasses;
249+
250+
[GenerateInterfaceAttribute]
251+
public class MyClass : IMyClass
252+
{
253+
public void GenerateString1<T, U, V>(T arg1, U? arg2, V arg3)
254+
=> Console.WriteLine($"{arg1} {arg2} {arg3}");
255+
256+
public T GenerateString2<T, U, V>(T arg1, U? arg2, V arg3)
257+
=> arg1;
258+
259+
public U? GenerateString3<T, U, V>(T arg1, U? arg2, V arg3)
260+
=> arg2;
261+
}
262+
""";
263+
264+
await TestHelper.VerifyAsync(source, SnapshotsDirectory);
265+
}
266+
267+
public class MyClass
268+
{
269+
public string MyMehod<T, U>(T ar, U asd)
270+
where T : class
271+
where U : struct
272+
{
273+
return "";
274+
}
275+
}
276+
277+
[Fact]
278+
public async Task WhenMethodHasMultipleGenericTypesWithTypeConstraints_AssertGenericTypesInInterface()
279+
{
280+
var source = """
281+
using ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.Attributes;
282+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.UnitTestClasses;
283+
284+
[GenerateInterfaceAttribute]
285+
public class MyClass : IMyClass
286+
{
287+
public void GenerateString1<T, U, V>(T arg1, U? arg2, V arg3)
288+
where T : class
289+
where U : struct
290+
=> Console.WriteLine($"{arg1} {arg2} {arg3}");
291+
292+
public T GenerateString2<T, U, V>(T arg1, U? arg2, V arg3)
293+
where T : class
294+
=> arg1;
295+
296+
public U? GenerateString3<T, U, V>(T arg1, U? arg2, V arg3)
297+
where U : struct
298+
=> arg2;
299+
}
300+
""";
301+
302+
await TestHelper.VerifyAsync(source, SnapshotsDirectory);
303+
}
214304
}
215305
#pragma warning restore IDE0058 // Expression value is never used
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//HintName: GenerateInterfaceAttribute.g.cs
2+
3+
#nullable enable
4+
using System;
5+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.Attributes
6+
{
7+
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
8+
public class GenerateInterfaceAttribute : Attribute
9+
{
10+
/// <summary>
11+
/// Set this to override the default interface name. Or leave it null to use the class name with an 'I' prepended to it.
12+
/// </summary>
13+
public string? InterfaceName { get; set; }
14+
15+
/// <summary>
16+
/// Set this to override the namespace to generate the interface in. By default, it will be the same as the class.
17+
/// </summary>
18+
public string? Namespace { get; set; }
19+
20+
/// <summary>
21+
/// Set this to specify the interfaces the generated interface will inherit from. For example, IDisposable.
22+
/// This should be a syntax-valid list as you would type it out normally because it will be concatenated directly into the interface definition.
23+
/// For example: "MyNamespace.MyInterface1, MyNamespace.MyInterface2"
24+
/// </summary>
25+
public string? Interfaces { get; set; }
26+
27+
/// <summary>
28+
/// Set this to specify the generates interface inherits from System.IDisposable.
29+
/// This will be appended to the list of interfaces.
30+
/// If you are also specifying interfaces with the "Interfaces" property, either set this to false and include "System.IDisposable" in the "Interfaces" property string, or set this to true and don't include "System.IDisposable" in the "Interfaces" property string.
31+
/// </summary>
32+
public bool IsIDisposable { get; set; } = false;
33+
}
34+
35+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)]
36+
public class ExcludeFromGeneratedInterfaceAttribute : Attribute
37+
{
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//HintName: IMyClass.g.cs
2+
#nullable enable
3+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.UnitTestClasses;
4+
5+
public interface IMyClass
6+
{
7+
void GenerateString1<T>(T arg1, T? arg2);
8+
T GenerateString2<T>(T arg1, T? arg2);
9+
T? GenerateString3<T>(T arg1, T? arg2);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//HintName: GenerateInterfaceAttribute.g.cs
2+
3+
#nullable enable
4+
using System;
5+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.Attributes
6+
{
7+
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
8+
public class GenerateInterfaceAttribute : Attribute
9+
{
10+
/// <summary>
11+
/// Set this to override the default interface name. Or leave it null to use the class name with an 'I' prepended to it.
12+
/// </summary>
13+
public string? InterfaceName { get; set; }
14+
15+
/// <summary>
16+
/// Set this to override the namespace to generate the interface in. By default, it will be the same as the class.
17+
/// </summary>
18+
public string? Namespace { get; set; }
19+
20+
/// <summary>
21+
/// Set this to specify the interfaces the generated interface will inherit from. For example, IDisposable.
22+
/// This should be a syntax-valid list as you would type it out normally because it will be concatenated directly into the interface definition.
23+
/// For example: "MyNamespace.MyInterface1, MyNamespace.MyInterface2"
24+
/// </summary>
25+
public string? Interfaces { get; set; }
26+
27+
/// <summary>
28+
/// Set this to specify the generates interface inherits from System.IDisposable.
29+
/// This will be appended to the list of interfaces.
30+
/// If you are also specifying interfaces with the "Interfaces" property, either set this to false and include "System.IDisposable" in the "Interfaces" property string, or set this to true and don't include "System.IDisposable" in the "Interfaces" property string.
31+
/// </summary>
32+
public bool IsIDisposable { get; set; } = false;
33+
}
34+
35+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)]
36+
public class ExcludeFromGeneratedInterfaceAttribute : Attribute
37+
{
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//HintName: IMyClass.g.cs
2+
#nullable enable
3+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.UnitTestClasses;
4+
5+
public interface IMyClass
6+
{
7+
void GenerateString1<T, U, V>(T arg1, U? arg2, V arg3);
8+
T GenerateString2<T, U, V>(T arg1, U? arg2, V arg3);
9+
U? GenerateString3<T, U, V>(T arg1, U? arg2, V arg3);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//HintName: GenerateInterfaceAttribute.g.cs
2+
3+
#nullable enable
4+
using System;
5+
namespace ProgrammerAl.SourceGenerators.PublicInterfaceGenerator.Attributes
6+
{
7+
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
8+
public class GenerateInterfaceAttribute : Attribute
9+
{
10+
/// <summary>
11+
/// Set this to override the default interface name. Or leave it null to use the class name with an 'I' prepended to it.
12+
/// </summary>
13+
public string? InterfaceName { get; set; }
14+
15+
/// <summary>
16+
/// Set this to override the namespace to generate the interface in. By default, it will be the same as the class.
17+
/// </summary>
18+
public string? Namespace { get; set; }
19+
20+
/// <summary>
21+
/// Set this to specify the interfaces the generated interface will inherit from. For example, IDisposable.
22+
/// This should be a syntax-valid list as you would type it out normally because it will be concatenated directly into the interface definition.
23+
/// For example: "MyNamespace.MyInterface1, MyNamespace.MyInterface2"
24+
/// </summary>
25+
public string? Interfaces { get; set; }
26+
27+
/// <summary>
28+
/// Set this to specify the generates interface inherits from System.IDisposable.
29+
/// This will be appended to the list of interfaces.
30+
/// If you are also specifying interfaces with the "Interfaces" property, either set this to false and include "System.IDisposable" in the "Interfaces" property string, or set this to true and don't include "System.IDisposable" in the "Interfaces" property string.
31+
/// </summary>
32+
public bool IsIDisposable { get; set; } = false;
33+
}
34+
35+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)]
36+
public class ExcludeFromGeneratedInterfaceAttribute : Attribute
37+
{
38+
}
39+
}

0 commit comments

Comments
 (0)