From 3e7f0b46d48dbe66a48005c127c879dc47b3cfd2 Mon Sep 17 00:00:00 2001 From: Ivan Murashka Date: Sat, 6 Sep 2025 13:28:16 +0200 Subject: [PATCH 1/2] Allow class unions --- .../Shapes/Attributes/ExposeAttribute.cs | 2 +- .../Shapes/Attributes/UnionAttribute.cs | 2 +- .../Shapes/Shape.cs | 5 +---- .../Shapes/ShapePropertyTests.cs | 6 +++--- .../Expose/ExposeAttributeGenerator.cs | 14 +++++++------- .../Expose/ExposeAttributePartInput.cs | 2 +- .../Expose/Parts/ExposeDeclarationPart.cs | 14 +++++++++++++- .../Union/Parts/UnionDeclarationPart.cs | 12 +++++++++++- .../Union/UnionAttributeAnalyzer.cs | 12 ++++++------ .../Union/UnionAttributeGenerator.cs | 6 +++--- .../Union/UnionAttributePartInput.cs | 2 +- Runtime/Appegy.Union.Generator.dll | 4 ++-- Runtime/Appegy.Union.Generator.pdb | Bin 27300 -> 27444 bytes 13 files changed, 50 insertions(+), 31 deletions(-) diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/ExposeAttribute.cs b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/ExposeAttribute.cs index 796324a..36b153d 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/ExposeAttribute.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/ExposeAttribute.cs @@ -2,7 +2,7 @@ namespace Appegy.Union { - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false)] public class ExposeAttribute : Attribute { public Type[] Interfaces { get; } diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/UnionAttribute.cs b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/UnionAttribute.cs index 50c1eba..5257351 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/UnionAttribute.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Attributes/UnionAttribute.cs @@ -2,7 +2,7 @@ namespace Appegy.Union { - [AttributeUsage(System.AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + [AttributeUsage(System.AttributeTargets.Struct | AttributeTargets.Class, Inherited = false)] public class UnionAttribute : System.Attribute { public System.Type[] Types { get; } diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs index cb510cb..0e80025 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace Appegy.Union.Generator.Shapes; +namespace Appegy.Union.Generator.Shapes; public interface IShape { @@ -9,5 +7,4 @@ public interface IShape [Union(typeof(Circle), typeof(Rectangle), typeof(Hexagon))] [Expose(typeof(IShape))] -[StructLayout(LayoutKind.Explicit, Pack = 1)] public partial struct Shape; \ No newline at end of file diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator.Tests/Shapes/ShapePropertyTests.cs b/Appegy.Union.Generator~/Appegy.Union.Generator.Tests/Shapes/ShapePropertyTests.cs index d89a5dc..4f8b98c 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator.Tests/Shapes/ShapePropertyTests.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator.Tests/Shapes/ShapePropertyTests.cs @@ -36,7 +36,7 @@ public void WhenGettingCircle_AndTypeIsNotCircle_ThenThrowsException() public void WhenSettingCircle_ThenTypeIsCircle() { // Arrange - var shape = new Shape(); + var shape = new Shape(new Hexagon()); var circle = new Circle(5); // Act @@ -76,7 +76,7 @@ public void WhenGettingRectangle_AndTypeIsNotRectangle_ThenThrowsException() public void WhenSettingRectangle_ThenTypeIsRectangle() { // Arrange - var shape = new Shape(); + var shape = new Shape(new Hexagon()); var rectangle = new Rectangle(4, 6); // Act @@ -116,7 +116,7 @@ public void WhenGettingHexagon_AndTypeIsNotHexagon_ThenThrowsException() public void WhenSettingHexagon_ThenTypeIsHexagon() { // Arrange - var shape = new Shape(); + var shape = new Shape(new Circle()); var hexagon = new Hexagon(3); // Act diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributeGenerator.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributeGenerator.cs index 9693b3c..efdeb92 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributeGenerator.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributeGenerator.cs @@ -36,11 +36,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .SyntaxProvider .ForAttributeWithMetadataName( ExposeAttributeName, - predicate: static (syntax, _) => syntax is StructDeclarationSyntax, + predicate: static (syntax, _) => syntax is TypeDeclarationSyntax, transform: static (ctx, _) => { - var syntax = (StructDeclarationSyntax)ctx.TargetNode; - var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax) as INamedTypeSymbol; + var syntax = (TypeDeclarationSyntax)ctx.TargetNode; + var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax); var attribute = ctx.Attributes.First(); var interfaces = attribute .GetTypesFromConstructor(TypeKind.Interface) @@ -53,14 +53,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .SyntaxProvider .ForAttributeWithMetadataName( UnionAttributeName, - predicate: static (syntax, _) => syntax is StructDeclarationSyntax, + predicate: static (syntax, _) => syntax is TypeDeclarationSyntax, transform: static (ctx, _) => { - var syntax = (StructDeclarationSyntax)ctx.TargetNode; - var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax) as INamedTypeSymbol; + var syntax = (TypeDeclarationSyntax)ctx.TargetNode; + var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax); var attribute = ctx.Attributes.First(); var types = attribute - .GetTypesFromConstructor(TypeKind.Struct) + .GetTypesFromConstructor() .Select(c => new ExposeTypeInfo(c)) .ToImmutableList(); return (Symbol: symbol!, Types: types); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributePartInput.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributePartInput.cs index fa30373..01a46d0 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributePartInput.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/ExposeAttributePartInput.cs @@ -4,7 +4,7 @@ namespace Appegy.Union.Generator; -public record struct ExposeAttributePartInput(StructDeclarationSyntax Syntax, IReadOnlyList Types, IReadOnlyList Interfaces); +public record struct ExposeAttributePartInput(TypeDeclarationSyntax Syntax, IReadOnlyList Types, IReadOnlyList Interfaces); public readonly struct ExposeTypeInfo(INamedTypeSymbol symbol) { diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/Parts/ExposeDeclarationPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/Parts/ExposeDeclarationPart.cs index 1291f2d..c2e4bb4 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/Parts/ExposeDeclarationPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Expose/Parts/ExposeDeclarationPart.cs @@ -1,4 +1,5 @@ using System.CodeDom.Compiler; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Appegy.Union.Generator; @@ -8,7 +9,18 @@ public override void Generate(IndentedTextWriter codeWriter, ExposeAttributePart { var (syntax, _, interfaces) = input; - codeWriter.Write("partial struct "); + codeWriter.Write("partial "); + + switch (syntax) + { + case StructDeclarationSyntax: + codeWriter.Write("struct "); + break; + case ClassDeclarationSyntax: + codeWriter.Write("class "); + break; + } + codeWriter.Write(syntax.Identifier.Text); codeWriter.WriteLine(" :"); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs index 653a00d..cc123f4 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/Parts/UnionDeclarationPart.cs @@ -1,4 +1,5 @@ using System.CodeDom.Compiler; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Appegy.Union.Generator; @@ -10,7 +11,16 @@ public override void Generate(IndentedTextWriter codeWriter, UnionAttributePartI var types = input.Types; codeWriter.WriteLine(AttributesNames.GeneratedCodeAttribute); - codeWriter.Write("partial struct "); + codeWriter.Write("partial "); + switch (syntax) + { + case StructDeclarationSyntax: + codeWriter.Write("struct "); + break; + case ClassDeclarationSyntax: + codeWriter.Write("class "); + break; + } codeWriter.WriteLine(syntax.Identifier.Text); codeWriter.Write(" : global::System.IEquatable<"); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs index 52fe31f..f900c50 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs @@ -45,32 +45,32 @@ private void AnalyzeAttribute(SyntaxNodeAnalysisContext context) private static void VerifyPartialModifier(SyntaxNodeAnalysisContext context, AttributeSyntax attributeSyntax) { - if (attributeSyntax.Parent?.Parent is not StructDeclarationSyntax structDeclaration) + if (attributeSyntax.Parent?.Parent is not TypeDeclarationSyntax typeDeclaration) { return; } - if (structDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword)) + if (typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword)) { return; } var diagnostic = Diagnostic.Create( NotPartial, - structDeclaration.Identifier.GetLocation(), - structDeclaration.Identifier.Text); + typeDeclaration.Identifier.GetLocation(), + typeDeclaration.Identifier.Text); context.ReportDiagnostic(diagnostic); } private static void VerifyParentsPartial(SyntaxNodeAnalysisContext context, AttributeSyntax attributeSyntax) { - if (attributeSyntax.Parent?.Parent is not StructDeclarationSyntax structDeclaration) + if (attributeSyntax.Parent?.Parent is not TypeDeclarationSyntax typeDeclaration) { return; } - var parent = structDeclaration.Parent; + var parent = typeDeclaration.Parent; while (parent is TypeDeclarationSyntax parentType) { if (!parentType.Modifiers.Any(SyntaxKind.PartialKeyword)) diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs index 81e9d92..f50fc7c 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeGenerator.cs @@ -40,14 +40,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .SyntaxProvider .ForAttributeWithMetadataName( UnionAttributeName, - predicate: static (syntax, _) => syntax is StructDeclarationSyntax, + predicate: static (syntax, _) => syntax is TypeDeclarationSyntax, transform: static (ctx, _) => { - var syntax = (StructDeclarationSyntax)ctx.TargetNode; + var syntax = (TypeDeclarationSyntax)ctx.TargetNode; var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax); var attribute = ctx.Attributes.First(); var types = attribute - .GetTypesFromConstructor(TypeKind.Struct) + .GetTypesFromConstructor() .Select(c => new UnionTypeInfo(c)) .ToImmutableList(); diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs index 83ca80b..b7f3681 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributePartInput.cs @@ -4,7 +4,7 @@ namespace Appegy.Union.Generator; -public record struct UnionAttributePartInput(StructDeclarationSyntax Syntax, IReadOnlyList Types, bool ExplicitLayout); +public record struct UnionAttributePartInput(TypeDeclarationSyntax Syntax, IReadOnlyList Types, bool ExplicitLayout); public readonly struct UnionTypeInfo(INamedTypeSymbol symbol) { diff --git a/Runtime/Appegy.Union.Generator.dll b/Runtime/Appegy.Union.Generator.dll index 3d1d07a..f7568f8 100644 --- a/Runtime/Appegy.Union.Generator.dll +++ b/Runtime/Appegy.Union.Generator.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f674aa7f435e096cf702b4424fecc8de7c7a6fcdb162f2f1dd4b8fe1dcadbd2 -size 55808 +oid sha256:5583bcb93eed49f39c3cb7ba4eb70be46ba582e3605c0b8d682483744fdb73cd +size 56320 diff --git a/Runtime/Appegy.Union.Generator.pdb b/Runtime/Appegy.Union.Generator.pdb index 775af1825958cd34de9ad59ebd71eef1d0ff0d23..6383e0795e876b1430e1249b7206bd3375973f4b 100644 GIT binary patch delta 5467 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 Date: Sat, 6 Sep 2025 13:42:55 +0200 Subject: [PATCH 2/2] Allow class unions --- .../Shapes/Shape.cs | 5 ++- .../DiagnosticDescriptors.cs | 8 ----- .../Union/UnionAttributeAnalyzer.cs | 30 ------------------ Runtime/Appegy.Union.Generator.dll | 4 +-- Runtime/Appegy.Union.Generator.pdb | Bin 27444 -> 27124 bytes Runtime/ExposeAttribute.cs | 2 +- Runtime/UnionAttribute.cs | 2 +- 7 files changed, 8 insertions(+), 43 deletions(-) diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs index 0e80025..cb510cb 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator.Shapes/Shapes/Shape.cs @@ -1,4 +1,6 @@ -namespace Appegy.Union.Generator.Shapes; +using System.Runtime.InteropServices; + +namespace Appegy.Union.Generator.Shapes; public interface IShape { @@ -7,4 +9,5 @@ public interface IShape [Union(typeof(Circle), typeof(Rectangle), typeof(Hexagon))] [Expose(typeof(IShape))] +[StructLayout(LayoutKind.Explicit, Pack = 1)] public partial struct Shape; \ No newline at end of file diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/DiagnosticDescriptors.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/DiagnosticDescriptors.cs index 44785ec..ec8e717 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/DiagnosticDescriptors.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/DiagnosticDescriptors.cs @@ -6,14 +6,6 @@ public static class DiagnosticDescriptors { private const string Category = "Union"; - public static DiagnosticDescriptor NotStruct { get; } = new( - id: "UNION001", - title: "Type has to be struct", - messageFormat: "The type '{0}' must be a struct to use [Union].", - category: Category, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true); - public static DiagnosticDescriptor NotPartial { get; } = new( id: "UNION002", title: "Struct is not partial", diff --git a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs index f900c50..e46c04f 100644 --- a/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs +++ b/Appegy.Union.Generator~/Appegy.Union.Generator/Union/UnionAttributeAnalyzer.cs @@ -17,7 +17,6 @@ public class UnionAttributeAnalyzer : DiagnosticAnalyzer NotPartial, NestedNotPartial, NoTypesProvided, - NotStruct, DuplicateUnionType); public override void Initialize(AnalysisContext context) @@ -39,7 +38,6 @@ private void AnalyzeAttribute(SyntaxNodeAnalysisContext context) VerifyPartialModifier(context, attributeSyntax); VerifyParentsPartial(context, attributeSyntax); VerifyArgumentsExistence(context, attributeSyntax); - VerifyAllTypesAreStruct(context, attributeSyntax); VerifyNoDuplicate(context, attributeSyntax); } @@ -99,34 +97,6 @@ private void VerifyArgumentsExistence(SyntaxNodeAnalysisContext context, Attribu context.ReportDiagnostic(diagnostic); } - private void VerifyAllTypesAreStruct(SyntaxNodeAnalysisContext context, AttributeSyntax attributeSyntax) - { - var arguments = attributeSyntax.ArgumentList?.Arguments; - if (arguments == null || arguments.Value.Count == 0) - { - return; - } - - foreach (var argument in arguments) - { - if (argument.Expression is not TypeOfExpressionSyntax typeOfExpression) - { - continue; - } - - var typeInfo = context.SemanticModel.GetTypeInfo(typeOfExpression.Type); - if (typeInfo.Type?.TypeKind != TypeKind.Struct) - { - var diagnostic = Diagnostic.Create( - NotStruct, - typeOfExpression.Type.GetLocation(), - typeInfo.Type?.ToDisplayString() ?? "unknown"); - - context.ReportDiagnostic(diagnostic); - } - } - } - private void VerifyNoDuplicate(SyntaxNodeAnalysisContext context, AttributeSyntax attributeSyntax) { var arguments = attributeSyntax.ArgumentList?.Arguments; diff --git a/Runtime/Appegy.Union.Generator.dll b/Runtime/Appegy.Union.Generator.dll index f7568f8..7ccddaf 100644 --- a/Runtime/Appegy.Union.Generator.dll +++ b/Runtime/Appegy.Union.Generator.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5583bcb93eed49f39c3cb7ba4eb70be46ba582e3605c0b8d682483744fdb73cd -size 56320 +oid sha256:dfb6f14913fc4a5224b91604329dc26ddb6aceda20efd5c0f6b1d3cc263316c6 +size 55296 diff --git a/Runtime/Appegy.Union.Generator.pdb b/Runtime/Appegy.Union.Generator.pdb index 6383e0795e876b1430e1249b7206bd3375973f4b..aeedcdf1408a9a8894b94a5f0e61b0ff60100f20 100644 GIT binary patch delta 5674 zcmZ9Q30M?I7RO&R!wdt+Dff{9ROAu{kU?&c8@!^T29H4wuV^$z<2`8N5#xp9h0%y+ zU6v$xWRzgkNK8JnYfSdjs4;QX(db53v&1VKqigp6ni}nI$M65@{p!7{uBxuCZai>a zdho5ZE_w9e0HTORMEQY4rySr9eeKmr(l{52fz#o7gWJUXqXU063={ zocHo8MyXrqM zI?)-(In{}3p{0;_KaT0>&vNNZBhs8{I`lg9Ynlr!N_U}E>8@lkr*on!ZB6e+yJg>v z+S1+VI&@p`y>xdnWxCT$XkMlV?a1Udo-`nfOD`Id&80W}ESKFWgO{9q=)7F|(x5zM zKf00UM|bl4>AuVXbfrJhOGJS*kaq(GQG0$6orms1aRos%6S@rf6mmS6_7?_IJM@R( z-bGv=Lh(giNu#=M)o70kQ3yMkANeZVTw-V5!S9l^Y)n?zg#u7Y1JSPPyEAFEA1W;Ad@ z#lHa&kS~~r#J>rGox!*}a@-Gb7ZDFf8nY|dg!n;-nWr!b{)d9y!6)JKBKafgA=)RR zy_qY#P%#m~qawi@JOlm#!9L)B!B2w%knbxxXcQa(<{9;b0uc`qaenfdL%=-yg@QxD zFT;Ng*GFVMNT~U|zy>!4Y8I7RiEpg71NG&dK?`z>ne2f`ZXL3e3L&3U-Cx z3kpFzTEz2l{bv>42Q2P1B0j`Kc9M?ASJZYF+AxuP#B zcy@f>Gr=jM10J!RdBi;6EX0i>&i6Q5!QZO7I2G|X5aR)HydT&BgK>eF)4`DsRwq&dHzK0qM{rP z0^x5LJOCUHe*rWI4F-x1*9smC4uj7F7>oyRh=}XJLln&W>P5K@Bgc;iFbvPx5HfdF z@Cb*?6I^;NC#C`$pV3Z>60IpQ91{KW1F`x>>_>pF=0P~djAY&e3M#X+q z6o`b8;9ueYMer!_H%R7=)hp=G9&bd4%&#Lq7_kL{ z=YS)?{Ahc z8$1I(e~@r|HTX7szD*0EHKIM^H~K4&ET~nAp^Wv!93zOAbXr2!#5Sow`8e; zIlfH69ABpR~#n)j1j_`Q`8<5{1 zCiIfv_26XqJ^3)$hy;sB(4!aT&ET1c`HA=z@JjgJf*Zj$_Jqv8XR&3SL5dse}-C%O+lB0B7}vo*v!Q)QjwTP~RwL}BmBuMyrc5*ecTGA zK~1vr7Dx1x&^7qb_8Vh8>{DYC?3h#=?BPVx$^F1)q}A)>Yr(&oW`muO zZno)kG%KB)Ibf5m$wf%sP#;TN>V5gVb zV1Fy6P%V{~<-ndOGr{&LN4NMi9ooZsHn4|UpRGT&Vek&k&KaxM<~7eBKKk_SV}E3< ztOz=|`^itY9S@zc`oC~T(hs{>{nn=ZZF?SVOo_XtzO$%xj*GF6;ny!_-|oAz>dDC| z)*fM7)%r#8txG4G4*Op73fvnu!}Zb^6WbnM99A&WYI4(3K{QnTBN z2{U(w=RchNnfHUbe+1@Nx!#<0GxMBg-i3E)@n{b4-*C)4FEK1N{!aQ`c`Ujvd_I7`1xvM}g`O zGL|GBPg}mEzWTGQjk7vdy}9~KVCs^#cXMjBfj;f`D?)=qPkp>8VYRvC(b?8XA4l&k zcl+uG?@g8;r#@KKSeScqYstD#lREcb|K!uBQ7h^mTMxy3cj|0Xc=McsIp-T&zuWTN z>9=(snIB)8`Qhzt=Hy;m%WrI~4tuLR3{V^uZ?0T!{=)SGhq2+aB4z(`5 zl|9F=f4MbuP+?*~*_!t}%T64KHKNokl9Pad4_Q#C5l~Hk5zm7V7Xk+{I*!a^PPG28cd;F*rZ}~Q=x_45ou`XeJ za!O5dYR&lCl$5IE)P#h-)rLf4b*iynO?9$yd{T8?O`>&dv@JNNFQ1fZ&vdy6NRx}O z{&EqOFBgVlx#*WA7rEJTQNjn9rbL%zSy_}P)mpwTI^exrYS|^V9FtmVrIxRxmRr*M zZq^OOPaQ+{K9XAG;66`|?+bdKvMGIUU`lgO^MZW}blNzz$FR9V^mgUMQKZm`KEWeG4_c$&mcXi3*uo_=lDLKl`tS2gu zbh)*b8>R!7-vQK~~ SY~5AYCTYd0S;tL0=kPx%0hQqZ delta 5824 zcmY+I30zdw9>@Q8W|(2vHvwUQ5mX#dnHi8(U_g-7H8*l&aX}?5TynvNGS^%i7pPQT zmhVxTJcmq6{ZPHk(iWG@%7wDb%5vw`^QrImznszg#?SA6{@?TepL5SW=iGBI-aaL@ zo{`okjXT$sDCQZWFT;qAtKdI2r*_)Rsj~^cS0acsC}T!GMosXW*e@7al!ww5Np!mu zoLe)i5_~HmZfj9v-jKKS|M_%8_OJ8yrd_Pxus+n3i`ZRAFA;^HEJn$FnouU8^n((i z%nc{PrKk6iwPRKEd1}yT<1nt%x-~f z=$cRm$_}$*gT`ogqkDiriyMVmwA2%-hCYPSty=mLs^a@29UVy2(G4g(Nk@Z|byS?v zS-(HUoi0KlsqRz@y$Bi8IHo73Tzb&-bPrkrt%CF!p0oklp5aA1?HQctMY}V+X}|3I z&?gx_^l#|CV7LChG&|FmmO*PW{b+9{xACX)EG`3RQZ|=?q_c4uM5A)J>_XS&GMJ|E zB4YI(pZw4}4eBkMs&9@ZJ?vl0WHh5}0s6b5Z9 zbqv#lI{dV++KD9me3X5Kk0w1L7`5 z+%9i~{zym?3H;bH_XG3Y@w#V$lSP~d>|)n|vk^}bab9Daf={VCITi6Gi1C0po(5J) zn14@oQ2`fpNHa2F22IY@AW&yQXn8sv%#Sit!T<{5FZYeohlK0xI25*8?!AKOB~ ze0>VTRb3UlW))Bo67oa`-4J&%$E%?dJXrZ6&bywu0K68lF(O_F=9|fc24S9xM7}S0 zFa(ybj}wq+h$3OX;8HXQgTGbqAaFGNI%p{J2a68Z2_6dW2A>Bo49Cwf5!ZuVCV8TnO^A68~XJQl06ZyQU zW+|9A)stxNiZ`M^=BJTwK&(#id~ghSBDmI$goUERHdNMv7lB2d{g ze1(EJzEZ&)=V^BJ$8iTmc!MhasKPi~^$YpSgh9%mccq zVBSBs!0#yrbX&pP-yQJ#B7RN5)y_`>($tQiK%-L=TrAmFns^KIhOyvTee-$cRSC zci5fTPOTZd$gxl_uAm_#LY}4rJpt->fy_xshQM_P23)^U}*Yec2$+lgqqj46}p(BeCwvW{Yd(>Jk z+j`hNiEXeyC6c?2?)M{iJ>?}E;agK|u+1s;u-~OL!Fr~)!A?&lJ@;UgZ8_|a^d`NX zFWC;bolYJ)T9`o|dRmcT^x%5(WLxj4BU@&Z?30&{hGp4c3$p8B?_{^ZX6BH$j@ol< zuoH9ZVXx)3dCNWc=;-nQ8!YN!kLHmt&#n=6S-#CzXQwOq<$$<>^{{UaY=X@%AU_># zht<=L0weqruzEUEP#(aY&JS>>=L(tvbW~Z`279Qm9d>0A1?niO*a%xz+|WgyUf37R zWYE#3;&Rw;iko1&mlz{-w5!AxsUz?ECG{v4mNvou;LHx|qcMhD4c~UY{P5AnJ0pjt zCeJ*u-zy<^?QH*fi{^gQ|6R>j|Js%P zIla~#S=DQkXO6SM_>#t`_gH%E+c6sdtLdYYw{AH2f%TU|A=V*d&qTjH?83UjyDvG1 z_n4=$PYIg#-#OQ~mQUDL``6`L zhYVZHhB;w}oaX1#-aP76d%C)7#llnF7uR_FVrvMh_}I{DSGD&u8>>dNzrMS!%)E7n z+G@K$rDvDiPp13rs&yakS=Glwwbt$K#v`gzrmL?FZ}Z>yl*T$^+KBcGW7AvCzLc`r z>}zj6@Y|W&pU1_{_4(~v|IN-zPh4N!n45F*O5>Kpi9fge_{QP;Cimdw&eu)vlz#0Q z9ls!d{)LG(?=*b<{tF#@?RW3ZUj6&Tx>rh1Hy)2aR@V{!Z=VI5wy!NKTDqtDyk%05 z*S++JTTO{o$94wqyx%_l=8D);)!l!n`QS+Z17RPX`hHUCW&PR(>6eRwJ{s10=Z;fP zY~NKBpA)ffZcI_d@`Dc+eYg7dv+q8b@XVi4U%#_ygy~>e(f%aUTfL8!oYv%8epcC4 z*E}5qO;x=rt(9r1R!ddllztUSm1)VA#8h)XGrY-3m6K3dt%=ses#J^fgsI(7n#2dE z#=oCjbj^^9Zs~Gi8Xy;zJh@0Kl#84!xhTq(i(#2K!n7s&%vf!apUQD#V6If_NGfOz z+$c5gk(!T7&9zeVS*f{0dL!I z$6aLCI@LqQyGx#qiqR{)y(F#`{W`9XHhGFHzM8Z9m{3WBE;<~xvJh2^)=^fL;P;i> zq_blljkBR_mE!%ME<8@sTccM0^jr@L) zLLKWXyJ&5A0ZUPiH!J%FT2=U8r`~KN)q*s<8>9rs&z1So07svy>Cza7Z*i!jrK+oT z9v%c~s$*eMp(a5^2FH=29QTAee5gqEOPpU+t&m*z#53WnoV-uciRpAkP5nysKl8n+ AVE_OC diff --git a/Runtime/ExposeAttribute.cs b/Runtime/ExposeAttribute.cs index e84d18c..36b153d 100644 --- a/Runtime/ExposeAttribute.cs +++ b/Runtime/ExposeAttribute.cs @@ -2,7 +2,7 @@ namespace Appegy.Union { - [AttributeUsage(AttributeTargets.Struct)] + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false)] public class ExposeAttribute : Attribute { public Type[] Interfaces { get; } diff --git a/Runtime/UnionAttribute.cs b/Runtime/UnionAttribute.cs index e7df7b1..74bbc36 100644 --- a/Runtime/UnionAttribute.cs +++ b/Runtime/UnionAttribute.cs @@ -2,7 +2,7 @@ namespace Appegy.Union { - [AttributeUsage(AttributeTargets.Struct)] + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false)] public class UnionAttribute : Attribute { public Type[] Types { get; }