Skip to content

Commit 3e64ef6

Browse files
authored
Add support for Unbound Generic Types (#158)
* Use Avalonia 0.9.* * Use Avalonia 0.9.* as inline data * Update package identity tests * Remove extra file * Don't add global:: if the type is unbound * Support generic type parameters in Rx*Events classes * Support inheritance from generic base types * Approve the new generated API * Formatting fixes * Further formatting fixes * Use the GenerateName local function
1 parent 1571291 commit 3e64ef6

20 files changed

+1201
-27961
lines changed

src/Pharmacist.Core/Generation/Generators/InstanceEventGenerator.cs

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,40 @@ private static ClassDeclarationSyntax GenerateStaticClass(string namespaceName,
5050
.WithLeadingTrivia(XmlSyntaxFactory.GenerateSummarySeeAlsoComment("A class that contains extension methods to wrap events for classes contained within the {0} namespace.", namespaceName))
5151
.WithMembers(List<MemberDeclarationSyntax>(declarations.Select(declaration =>
5252
{
53-
var eventsClassName = IdentifierName("Rx" + declaration.Name + "Events");
54-
return MethodDeclaration(eventsClassName, Identifier("Events"))
53+
return BuildMethodDeclaration(declaration)
5554
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
5655
.WithParameterList(ParameterList(SingletonSeparatedList(
5756
Parameter(Identifier("item"))
5857
.WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword)))
5958
.WithType(IdentifierName(declaration.GenerateFullGenericName())))))
60-
.WithExpressionBody(ArrowExpressionClause(
61-
ObjectCreationExpression(eventsClassName)
62-
.WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(IdentifierName("item")))))))
6359
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
6460
.WithObsoleteAttribute(declaration)
6561
.WithLeadingTrivia(XmlSyntaxFactory.GenerateSummarySeeAlsoComment("A wrapper class which wraps all the events contained within the {0} class.", declaration.ConvertToDocument()));
62+
63+
static MethodDeclarationSyntax BuildMethodDeclaration(ITypeDefinition declaration)
64+
{
65+
if (declaration.IsUnboundGenericTypeDefinition())
66+
{
67+
var args = string.Join(", ", declaration.TypeArguments.Select(param => param.FullName));
68+
var genericEventsClassName = IdentifierName("Rx" + declaration.Name + "Events<" + args + ">");
69+
return MethodDeclaration(genericEventsClassName, Identifier("Events"))
70+
.WithTypeParameterList(TypeParameterList(
71+
Token(SyntaxKind.LessThanToken),
72+
SeparatedList(declaration.TypeArguments.Select(arg => TypeParameter(arg.FullName))),
73+
Token(SyntaxKind.GreaterThanToken)))
74+
.WithExpressionBody(ArrowExpressionClause(
75+
ObjectCreationExpression(genericEventsClassName)
76+
.WithArgumentList(ArgumentList(SingletonSeparatedList(
77+
Argument(IdentifierName("item")))))));
78+
}
79+
80+
var eventsClassName = IdentifierName("Rx" + declaration.Name + "Events");
81+
return MethodDeclaration(eventsClassName, Identifier("Events"))
82+
.WithExpressionBody(ArrowExpressionClause(
83+
ObjectCreationExpression(eventsClassName)
84+
.WithArgumentList(ArgumentList(SingletonSeparatedList(
85+
Argument(IdentifierName("item")))))));
86+
}
6687
})));
6788
}
6889

@@ -112,9 +133,29 @@ private static ClassDeclarationSyntax GenerateEventWrapperClass(ITypeDefinition
112133
.WithObsoleteAttribute(typeDefinition)
113134
.WithLeadingTrivia(XmlSyntaxFactory.GenerateSummarySeeAlsoComment("A class which wraps the events contained within the {0} class as observables.", typeDefinition.ConvertToDocument()));
114135

136+
if (typeDefinition.IsUnboundGenericTypeDefinition())
137+
{
138+
classDeclaration = classDeclaration.WithTypeParameterList(TypeParameterList(
139+
Token(SyntaxKind.LessThanToken),
140+
SeparatedList(typeDefinition.TypeArguments.Select(arg => TypeParameter(arg.FullName))),
141+
Token(SyntaxKind.GreaterThanToken)));
142+
}
143+
115144
if (baseTypeDefinition != null)
116145
{
117-
classDeclaration = classDeclaration.WithBaseList(BaseList(SingletonSeparatedList<BaseTypeSyntax>(SimpleBaseType(IdentifierName($"global::{baseTypeDefinition.Namespace}.Rx{baseTypeDefinition.Name}Events")))));
146+
var baseTypeName = $"global::{baseTypeDefinition.Namespace}.Rx{baseTypeDefinition.Name}Events";
147+
if (baseTypeDefinition.IsUnboundGenericTypeDefinition())
148+
{
149+
var directBaseType = typeDefinition.DirectBaseTypes
150+
.First(directBase => directBase.FullName == baseTypeDefinition.FullName);
151+
var argumentList = directBaseType.TypeArguments.Select(arg => arg.GenerateFullGenericName());
152+
var argumentString = "<" + string.Join(", ", argumentList) + ">";
153+
baseTypeName += argumentString;
154+
}
155+
156+
classDeclaration = classDeclaration.WithBaseList(BaseList(
157+
SingletonSeparatedList<BaseTypeSyntax>(SimpleBaseType(
158+
IdentifierName(baseTypeName)))));
118159
}
119160

120161
return classDeclaration;

src/Pharmacist.Core/Generation/ReflectionExtensions.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,26 @@ public static string GenerateFullGenericName(this IType currentType)
165165

166166
if (currentType.TypeParameterCount > 0)
167167
{
168-
sb.Append('<')
169-
.Append(string.Join(", ", currentType.TypeArguments.Select(GenerateFullGenericName)))
170-
.Append('>');
168+
var isUnbound = currentType.IsUnbound();
169+
var arguments = string.Join(", ", currentType.TypeArguments.Select(GenerateName));
170+
sb.Append('<').Append(arguments).Append('>');
171+
172+
string GenerateName(IType type) => isUnbound ? type.FullName : GenerateFullGenericName(type);
171173
}
172174

173175
return sb.ToString();
174176
}
175177

178+
/// <summary>
179+
/// Checks if the specified generic type definition is unbound.
180+
/// </summary>
181+
/// <param name="definition">The type to check properties for.</param>
182+
/// <returns>Returns true if type is an unbound generic type, otherwise false.</returns>
183+
public static bool IsUnboundGenericTypeDefinition(this ITypeDefinition definition)
184+
{
185+
return definition.TypeParameterCount > 0 && definition.IsUnbound();
186+
}
187+
176188
private static IEnumerable<ITypeDefinition> GetPublicTypeDefinitionsWithEvents(ICompilation compilation)
177189
{
178190
return _publicEventsTypeMapping.GetOrAdd(

0 commit comments

Comments
 (0)