diff --git a/Arch.AOT.SourceGenerator/Arch.AOT.SourceGenerator.csproj b/Arch.AOT.SourceGenerator/Arch.AOT.SourceGenerator.csproj index 6458d69..a6bf395 100644 --- a/Arch.AOT.SourceGenerator/Arch.AOT.SourceGenerator.csproj +++ b/Arch.AOT.SourceGenerator/Arch.AOT.SourceGenerator.csproj @@ -13,7 +13,7 @@ Arch.AOT.SourceGenerator Arch.AOT.SourceGenerator - 1.0.1 + 1.0.3-custom genaray Apache-2.0 A source generator for arch to support AOT. diff --git a/Arch.AOT.SourceGenerator/Extensions/StringBuilderExtensions.cs b/Arch.AOT.SourceGenerator/Extensions/StringBuilderExtensions.cs index 7fbac95..ca3d149 100644 --- a/Arch.AOT.SourceGenerator/Extensions/StringBuilderExtensions.cs +++ b/Arch.AOT.SourceGenerator/Extensions/StringBuilderExtensions.cs @@ -27,7 +27,7 @@ public static StringBuilder AppendComponentTypes(this StringBuilder sb, IList(SymbolEqualityComparer.Default); + var closedGenericTypes = new HashSet(SymbolEqualityComparer.Default); + foreach (var type in typeList) { // Get the symbol for the type. var symbol = ModelExtensions.GetDeclaredSymbol(compilation.GetSemanticModel(type.SyntaxTree), type); // If the symbol is not a type symbol, we can't do anything with it. - if (symbol is not ITypeSymbol typeSymbol) + if (symbol is not INamedTypeSymbol typeSymbol) { continue; } - - // Check if there are any fields in the type. - var hasZeroFields = true; - foreach (var member in typeSymbol.GetMembers()) + + // If the type is a generic Type definition (e.g. SomeType) we don't want to register it, we want to register their concrete usages instead. + if (!IsClosedType(typeSymbol)) { - if (member is not IFieldSymbol) continue; - - hasZeroFields = false; - break; + openGenericTypes.Add(typeSymbol); + continue; } - var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - _componentTypes.Add(new ComponentType(typeName, hasZeroFields, typeSymbol.IsValueType)); + AddComponentType(typeSymbol); } + + foreach (var tree in compilation.SyntaxTrees) + { + var semanticModel = compilation.GetSemanticModel(tree); + var root = tree.GetRoot(); + foreach (var node in root.DescendantNodes().OfType()) + { + if (semanticModel.GetSymbolInfo(node).Symbol is not INamedTypeSymbol typeSymbol) + continue; + + // This is a constructed instance of a generic component + if (openGenericTypes.Contains(typeSymbol.OriginalDefinition) && IsClosedType(typeSymbol)) + { + closedGenericTypes.Add(typeSymbol); + } + } + } + + foreach (var typeSymbol in closedGenericTypes) + { + AddComponentType(typeSymbol); + } + sb.AppendComponentTypes(_componentTypes); productionContext.AddSource("GeneratedComponentRegistry.g.cs",CSharpSyntaxTree.ParseText(sb.ToString()).GetRoot().NormalizeWhitespace().ToFullString()); } + + private static bool IsClosedType(INamedTypeSymbol typeSymbol) + { + if (typeSymbol.IsUnboundGenericType) return false; + if (typeSymbol.IsGenericType) + { + return typeSymbol.TypeArguments.All(t => t.TypeKind != TypeKind.TypeParameter); + } + + return true; + } + + private void AddComponentType(INamedTypeSymbol typeSymbol) + { + // Check if there are any fields in the type. + var hasZeroFields = true; + foreach (var member in typeSymbol.GetMembers()) + { + if (member is not IFieldSymbol) continue; + + hasZeroFields = false; + break; + } + + var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + _componentTypes.Add(new ComponentType(typeName, hasZeroFields, typeSymbol.IsValueType)); + } } \ No newline at end of file