From 02b624c238eb2bb510857745d69a4aefac4bae52 Mon Sep 17 00:00:00 2001 From: MeltyPlayer Date: Wed, 17 Apr 2024 01:52:55 -0500 Subject: [PATCH] Fixed some more bugs, around generics/tuples. --- Schema Tests/readOnly/MemberTests.cs | 106 ++++++++++++++++++- Schema/Schema.csproj | 2 +- Schema/src/readOnly/ReadOnlyTypeGenerator.cs | 71 ++++++++----- Schema/src/util/symbols/SymbolTypeUtil.cs | 29 ++++- Schema/src/util/types/BSymbolTypeV2.cs | 4 + Schema/src/util/types/ITypeV2.cs | 1 + Schema/src/util/types/SymbolBasedTypeV2.cs | 51 +++++---- Schema/src/util/types/TypeBasedTypeV2.cs | 3 + 8 files changed, 216 insertions(+), 51 deletions(-) diff --git a/Schema Tests/readOnly/MemberTests.cs b/Schema Tests/readOnly/MemberTests.cs index 6caf7b1..c51efff 100644 --- a/Schema Tests/readOnly/MemberTests.cs +++ b/Schema Tests/readOnly/MemberTests.cs @@ -42,7 +42,7 @@ public interface IReadOnlyWrapper { } [Test] - public void TestIEnumerable() { + public void TestGeneric() { ReadOnlyGeneratorTestUtil.AssertGenerated( """ using schema.readOnly; @@ -66,5 +66,109 @@ public interface IReadOnlyWrapper { """); } + + [Test] + public void TestNestedGeneric() { + ReadOnlyGeneratorTestUtil.AssertGenerated( + """ + using schema.readOnly; + using System.Collections.Generic; + + namespace foo.bar { + [GenerateReadOnly] + public partial interface IWrapper { + public IEnumerable> Value { get; set; } + } + } + """, + """ + namespace foo.bar { + public partial interface IWrapper : IReadOnlyWrapper; + + public interface IReadOnlyWrapper { + public System.Collections.Generic.IEnumerable> Value { get; } + } + } + + """); + } + + [Test] + public void TestIndexer() { + ReadOnlyGeneratorTestUtil.AssertGenerated( + """ + using schema.readOnly; + using System.Collections.Generic; + + namespace foo.bar { + [GenerateReadOnly] + public partial interface IWrapper { + public bool this[int index] { get; set; } + } + } + """, + """ + namespace foo.bar { + public partial interface IWrapper : IReadOnlyWrapper; + + public interface IReadOnlyWrapper { + public bool this[int index] { get; } + } + } + + """); + } + + [Test] + public void TestNamelessTuple() { + ReadOnlyGeneratorTestUtil.AssertGenerated( + """ + using schema.readOnly; + using System.Collections.Generic; + + namespace foo.bar { + [GenerateReadOnly] + public partial interface IWrapper { + public (bool, int) Tuple { get; set; } + } + } + """, + """ + namespace foo.bar { + public partial interface IWrapper : IReadOnlyWrapper; + + public interface IReadOnlyWrapper { + public (bool, int) Tuple { get; } + } + } + + """); + } + + [Test] + public void TestNamedTuple() { + ReadOnlyGeneratorTestUtil.AssertGenerated( + """ + using schema.readOnly; + using System.Collections.Generic; + + namespace foo.bar { + [GenerateReadOnly] + public partial interface IWrapper { + public (bool a, int b) Tuple { get; set; } + } + } + """, + """ + namespace foo.bar { + public partial interface IWrapper : IReadOnlyWrapper; + + public interface IReadOnlyWrapper { + public (bool a, int b) Tuple { get; } + } + } + + """); + } } } \ No newline at end of file diff --git a/Schema/Schema.csproj b/Schema/Schema.csproj index 34a9fa9..ffed34a 100644 --- a/Schema/Schema.csproj +++ b/Schema/Schema.csproj @@ -14,7 +14,7 @@ Library for converting classes to and from binary. Provides a C# Roslyn generator that automatically implements conversion logic for simple classes. schema schema - 0.4.2 + 0.4.3 MeltyPlayer diff --git a/Schema/src/readOnly/ReadOnlyTypeGenerator.cs b/Schema/src/readOnly/ReadOnlyTypeGenerator.cs index 4ceb3d1..1793891 100644 --- a/Schema/src/readOnly/ReadOnlyTypeGenerator.cs +++ b/Schema/src/readOnly/ReadOnlyTypeGenerator.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using schema.binary.parser; +using schema.util.asserts; using schema.util.generators; using schema.util.symbols; using schema.util.text; @@ -119,13 +120,12 @@ var constMembers return false; } - if (memberSymbol is IPropertySymbol { - IsWriteOnly: true - }) { + if (memberSymbol is IPropertySymbol) { return false; } if (memberSymbol is IMethodSymbol && + !memberSymbol.Name.StartsWith("get_") && !memberSymbol.HasAttribute()) { return false; } @@ -141,13 +141,10 @@ var constMembers cbsb.EnterBlock(blockPrefix); foreach (var parsedMember in constMembers) { - var (_, memberSymbol, memberTypeSymbol, _) = parsedMember; + var (_, memberSymbol, _, _) = parsedMember; - { - if (memberSymbol is IMethodSymbol methodSymbol) { - memberTypeSymbol = methodSymbol.ReturnType; - } - } + var methodSymbol = Asserts.AsA(memberSymbol); + var memberTypeSymbol = methodSymbol.ReturnType; cbsb.Write( SymbolTypeUtil.AccessibilityToModifier( @@ -158,20 +155,23 @@ var constMembers cbsb.Write(typeV2.GetQualifiedNameFromCurrentSymbol(memberTypeV2)); cbsb.Write(" "); - cbsb.Write(memberSymbol.Name.EscapeKeyword()); - - switch (memberSymbol) { - case IMethodSymbol methodSymbol: { - cbsb.Write(methodSymbol.TypeParameters.GetGenericParameters()); - cbsb.Write("("); - + var memberName = memberSymbol.Name; + // Property + if (memberName.StartsWith("get_")) { + if (methodSymbol.AssociatedSymbol?.Name != "this[]") { + cbsb.Write(memberSymbol.Name.Substring(4).EscapeKeyword()); + cbsb.WriteLine(" { get; }"); + } else { for (var i = 0; i < methodSymbol.Parameters.Length; ++i) { + cbsb.Write("this["); + if (i > 0) { cbsb.Write(", "); } var parameterSymbol = methodSymbol.Parameters[i]; - var parameterTypeV2 = TypeV2.FromSymbol(parameterSymbol.Type); + var parameterTypeV2 + = TypeV2.FromSymbol(parameterSymbol.Type); cbsb.Write( typeV2.GetQualifiedNameFromCurrentSymbol( parameterTypeV2)); @@ -179,17 +179,36 @@ var constMembers cbsb.Write(parameterSymbol.Name.EscapeKeyword()); } - cbsb.Write(")"); - cbsb.Write( - methodSymbol.TypeParameters.GetTypeConstraints(typeV2)); - - cbsb.WriteLine(";"); - break; + cbsb.WriteLine("] { get; }"); } - case IPropertySymbol: { - cbsb.WriteLine(" { get; }"); - break; + } + // Method + else { + cbsb.Write(memberSymbol.Name.EscapeKeyword()); + cbsb.Write(methodSymbol.TypeParameters + .GetGenericParameters()); + cbsb.Write("("); + + for (var i = 0; i < methodSymbol.Parameters.Length; ++i) { + if (i > 0) { + cbsb.Write(", "); + } + + var parameterSymbol = methodSymbol.Parameters[i]; + var parameterTypeV2 + = TypeV2.FromSymbol(parameterSymbol.Type); + cbsb.Write( + typeV2.GetQualifiedNameFromCurrentSymbol( + parameterTypeV2)); + cbsb.Write(" "); + cbsb.Write(parameterSymbol.Name.EscapeKeyword()); } + + cbsb.Write(")"); + cbsb.Write( + methodSymbol.TypeParameters.GetTypeConstraints(typeV2)); + + cbsb.WriteLine(";"); } } diff --git a/Schema/src/util/symbols/SymbolTypeUtil.cs b/Schema/src/util/symbols/SymbolTypeUtil.cs index d023349..cfd8d5d 100644 --- a/Schema/src/util/symbols/SymbolTypeUtil.cs +++ b/Schema/src/util/symbols/SymbolTypeUtil.cs @@ -10,6 +10,7 @@ using schema.binary; using schema.binary.attributes; using schema.binary.parser; +using schema.util.asserts; using schema.util.diagnostics; using schema.util.types; @@ -369,13 +370,37 @@ public static string GetQualifiedNameFromCurrentSymbol( return overrideName ?? referencedSymbol.Name.EscapeKeyword(); } + var sb = new StringBuilder(); + + if (referencedSymbol is + { Name: "ValueTuple", FullyQualifiedNamespace: "System" }) { + sb.Append("("); + + var genericArguments = referencedSymbol.GetTupleElements().ToArray(); + for (var i = 0; i < genericArguments.Length; ++i) { + if (i > 0) { + sb.Append(", "); + } + + var (tupleItemName, tupleItemType) = genericArguments[i]; + sb.Append(sourceSymbol + .GetQualifiedNameFromCurrentSymbol(tupleItemType)); + if (tupleItemName.Length > 0 && tupleItemName != $"Item{1 + i}") { + sb.Append(" "); + sb.Append(tupleItemName); + } + } + + sb.Append(")"); + + return sb.ToString(); + } + var currentNamespace = sourceSymbol.NamespaceParts.Select(EscapeKeyword).ToArray(); var referencedNamespace = referencedSymbol.NamespaceParts.Select(EscapeKeyword).ToArray(); - var sb = new StringBuilder(); - string mergedNamespaceText; if (currentNamespace.Length == 0 && referencedNamespace.Length == 0) { mergedNamespaceText = ""; diff --git a/Schema/src/util/types/BSymbolTypeV2.cs b/Schema/src/util/types/BSymbolTypeV2.cs index 6c38100..20d9b3a 100644 --- a/Schema/src/util/types/BSymbolTypeV2.cs +++ b/Schema/src/util/types/BSymbolTypeV2.cs @@ -38,6 +38,9 @@ public abstract bool HasGenericArguments( public abstract bool IsGenericTypeParameter( out IEnumerable genericConstraints); + public abstract IEnumerable<(string, ITypeV2)> GetTupleElements(); + + public abstract bool ContainsMemberWithType(ITypeV2 other); public abstract bool HasAttribute() @@ -69,6 +72,7 @@ public string FullyQualifiedName { return $"{namespacePortion}{declaringTypesPortion}{name}"; } } + private bool Matches_(string name, string? fullyQualifiedNamespace, int genericArgCount) diff --git a/Schema/src/util/types/ITypeV2.cs b/Schema/src/util/types/ITypeV2.cs index 29f273c..1e9c4fa 100644 --- a/Schema/src/util/types/ITypeV2.cs +++ b/Schema/src/util/types/ITypeV2.cs @@ -31,6 +31,7 @@ public interface ITypeV2 { IEnumerable GenericArguments { get; } bool IsGenericTypeParameter(out IEnumerable genericConstraints); IEnumerable GenericConstraints { get; } + IEnumerable<(string, ITypeV2)> GetTupleElements(); bool HasNullableAnnotation { get; } bool IsAtLeastAsBinaryConvertibleAs(ITypeV2 other); diff --git a/Schema/src/util/types/SymbolBasedTypeV2.cs b/Schema/src/util/types/SymbolBasedTypeV2.cs index 91ca0b6..1f30e07 100644 --- a/Schema/src/util/types/SymbolBasedTypeV2.cs +++ b/Schema/src/util/types/SymbolBasedTypeV2.cs @@ -153,12 +153,18 @@ public override bool IsGenericTypeParameter( genericConstraints = typeParameterSymbol - .ConstraintTypes - .Where(constraint => constraint is not IErrorTypeSymbol) - .Select(TypeV2.FromSymbol); + .ConstraintTypes + .Where(constraint => constraint is not IErrorTypeSymbol) + .Select(TypeV2.FromSymbol); return true; } + public override IEnumerable<(string, ITypeV2)> GetTupleElements() + => (this.symbol_ as INamedTypeSymbol)!.TupleElements.Select( + fieldSymbol => (fieldSymbol.Name, + TypeV2.FromSymbol(fieldSymbol.Type))); + + public override bool HasNullableAnnotation => this.symbol_.NullableAnnotation == NullableAnnotation.Annotated; @@ -172,16 +178,17 @@ public override TAttribute GetAttribute() public override IEnumerable GetAttributes() => this.symbol_.GetAttributeData() .Select(attributeData => { - var attribute = - attributeData.Instantiate(this.symbol_); - if (attribute is BMemberAttribute memberAttribute) { - memberAttribute.Init(this.diagnosticReporter_, - this.symbol_.ContainingType, - this.symbol_.Name); - } - - return attribute; - }); + var attribute = + attributeData + .Instantiate(this.symbol_); + if (attribute is BMemberAttribute memberAttribute) { + memberAttribute.Init(this.diagnosticReporter_, + this.symbol_.ContainingType, + this.symbol_.Name); + } + + return attribute; + }); private IEnumerable GetAttributeData_() where TAttribute : Attribute { @@ -190,20 +197,22 @@ private IEnumerable GetAttributeData_() .GetAttributes() .Where(attributeData => attributeData.AttributeClass?.IsExactlyType( - attributeType) ?? false); + attributeType) ?? + false); } public override bool ContainsMemberWithType(ITypeV2 other) => this.symbol_ .GetMembers() .Select(member => { - switch (member) { - case IFieldSymbol fieldSymbol: return fieldSymbol.Type; - case IPropertySymbol propertySymbol: - return propertySymbol.Type; - default: return null; - } - }) + switch (member) { + case IFieldSymbol fieldSymbol: + return fieldSymbol.Type; + case IPropertySymbol propertySymbol: + return propertySymbol.Type; + default: return null; + } + }) .Where(type => type != null) .Distinct() .Select(TypeV2.FromSymbol) diff --git a/Schema/src/util/types/TypeBasedTypeV2.cs b/Schema/src/util/types/TypeBasedTypeV2.cs index a163b46..d8504ee 100644 --- a/Schema/src/util/types/TypeBasedTypeV2.cs +++ b/Schema/src/util/types/TypeBasedTypeV2.cs @@ -160,6 +160,9 @@ public override bool IsGenericTypeParameter( return true; } + public override IEnumerable<(string, ITypeV2)> GetTupleElements() + => throw new NotImplementedException(); + private SchemaPrimitiveType GetPrimitiveType_(Type type) { if (type.IsEnum) { return SchemaPrimitiveType.ENUM;