Skip to content

Commit

Permalink
Fix module generator bug with self-referential type args (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasongin authored Apr 5, 2024
1 parent a0fd255 commit acc2c57
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 6 deletions.
24 changes: 18 additions & 6 deletions src/NodeApi.Generator/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,19 @@ static PropertyInfo GetAttributeProperty(Type type, string name)
return typeBuilder.CreateType()!;
}

private static void BuildBaseTypeAndInterfaces(INamedTypeSymbol typeSymbol)
/// <summary>
/// Ensures that a type symbol's base type, interface types, and type arguments (if any)
/// are built before building the target type.
/// </summary>
/// <param name="typeSymbol">The symbol that is about to be built as a type.</param>
/// <param name="selfSymbol">The type symbol that referenced the current one; used to detect
/// self-referential type args. (Should only be specified with recursive calls.)</param>
private static void BuildBaseTypeAndInterfaces(
INamedTypeSymbol typeSymbol, INamedTypeSymbol? selfSymbol = null)
{
static void BuildType(INamedTypeSymbol typeSymbol)
static void BuildType(INamedTypeSymbol typeSymbol, INamedTypeSymbol selfSymbol)
{
BuildBaseTypeAndInterfaces(typeSymbol);
BuildBaseTypeAndInterfaces(typeSymbol, selfSymbol);

string typeFullName = GetTypeSymbolFullName(typeSymbol);
if (SymbolicTypes.TryGetValue(typeFullName, out Type? type) &&
Expand All @@ -351,18 +359,22 @@ static void BuildType(INamedTypeSymbol typeSymbol)

if (typeSymbol.BaseType != null)
{
BuildType(typeSymbol.BaseType);
BuildType(typeSymbol.BaseType, typeSymbol);
}

foreach (INamedTypeSymbol interfaceTypeSymbol in typeSymbol.Interfaces)
{
BuildType(interfaceTypeSymbol);
BuildType(interfaceTypeSymbol, typeSymbol);
}

foreach (INamedTypeSymbol typeArgSymbol in
typeSymbol.TypeArguments.OfType<INamedTypeSymbol>())
{
BuildType(typeArgSymbol);
// Skip self-referential type parameters.
if (!SymbolEqualityComparer.Default.Equals(typeArgSymbol, selfSymbol))
{
BuildType(typeArgSymbol, typeSymbol);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions test/TestCases/napi-dotnet/ComplexTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,12 @@ public class CollectionOfClassObjects : IEnumerable<ClassObject>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
=> throw new NotImplementedException();
}

// Ensure module generation handles a self-referential interface type argument.
[JSExport]
public class Equatable : IEquatable<Equatable>
{
public bool Equals(Equatable? other) => other != null;
public override bool Equals(object obj) => obj is Equatable;

Check warning on line 258 in test/TestCases/napi-dotnet/ComplexTypes.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Nullability of type of parameter 'obj' doesn't match overridden member (possibly because of nullability attributes).

Check warning on line 258 in test/TestCases/napi-dotnet/ComplexTypes.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Nullability of type of parameter 'obj' doesn't match overridden member (possibly because of nullability attributes).
public override int GetHashCode() => 1;
}

0 comments on commit acc2c57

Please sign in to comment.