From ef35e25e12717697de6cfe094174f0e950c75e7e Mon Sep 17 00:00:00 2001 From: Paul Braetz Date: Sun, 4 Jan 2026 04:02:50 +0100 Subject: [PATCH 1/2] remove empty project --- .../CSharpSourceBuilder.TestApplication.csproj | 15 --------------- CSharpSourceBuilder.TestApplication/Program.cs | 4 ---- RhoMicro.CodeAnalysis.slnx | 1 - 3 files changed, 20 deletions(-) delete mode 100644 CSharpSourceBuilder.TestApplication/CSharpSourceBuilder.TestApplication.csproj delete mode 100644 CSharpSourceBuilder.TestApplication/Program.cs diff --git a/CSharpSourceBuilder.TestApplication/CSharpSourceBuilder.TestApplication.csproj b/CSharpSourceBuilder.TestApplication/CSharpSourceBuilder.TestApplication.csproj deleted file mode 100644 index 2fc683e..0000000 --- a/CSharpSourceBuilder.TestApplication/CSharpSourceBuilder.TestApplication.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net9.0 - preview - enable - enable - - - - - - - diff --git a/CSharpSourceBuilder.TestApplication/Program.cs b/CSharpSourceBuilder.TestApplication/Program.cs deleted file mode 100644 index be6129a..0000000 --- a/CSharpSourceBuilder.TestApplication/Program.cs +++ /dev/null @@ -1,4 +0,0 @@ -// See https://aka.ms/new-console-template for more information - -using RhoMicro.CodeAnalysis; - diff --git a/RhoMicro.CodeAnalysis.slnx b/RhoMicro.CodeAnalysis.slnx index 40817fd..bb16803 100644 --- a/RhoMicro.CodeAnalysis.slnx +++ b/RhoMicro.CodeAnalysis.slnx @@ -1,6 +1,5 @@ - From 366fd9f84a47085ff928f6d899be016c13cca7d0 Mon Sep 17 00:00:00 2001 From: Paul Braetz Date: Sun, 4 Jan 2026 18:28:11 +0100 Subject: [PATCH 2/2] allow unmanaged union type variants --- .../Models/UnionTypeAttribute.Model.cs | 86 ++++++++++++++++++- Janus.Tests.EndToEnd/UnionTypeVariantTest.cs | 20 +++++ 2 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 Janus.Tests.EndToEnd/UnionTypeVariantTest.cs diff --git a/Janus.Analyzers/Models/UnionTypeAttribute.Model.cs b/Janus.Analyzers/Models/UnionTypeAttribute.Model.cs index 66250d0..cf82d14 100644 --- a/Janus.Analyzers/Models/UnionTypeAttribute.Model.cs +++ b/Janus.Analyzers/Models/UnionTypeAttribute.Model.cs @@ -92,7 +92,7 @@ private void Initialize(ITypeSymbol variant, CancellationToken ct) Type = new( isArray ? VariantTypeKind.Reference - : actualVariant.IsUnmanagedType + : actualVariant.IsUnmanagedOrUnmanagedUnionType ? VariantTypeKind.Unmanaged : VariantTypeKind.Value, IsNullable: true, @@ -106,7 +106,7 @@ private void Initialize(ITypeSymbol variant, CancellationToken ct) var name = variant.ToDisplayString(TypeDisplayFormat); Type = extractedVariant switch { - { IsUnmanagedType: true } => + { IsUnmanagedOrUnmanagedUnionType: true } => new(isArray ? VariantTypeKind.Reference : VariantTypeKind.Unmanaged, @@ -172,3 +172,85 @@ public override Boolean IsNullable set => base.IsNullable = value; } } + +file static class Extensions +{ + extension(ITypeSymbol symbol) + { + public Boolean IsUnmanagedOrUnmanagedUnionType + { + get + { + var result = isUnmanaged(symbol, null); + + return result; + + Boolean isUnmanaged(ITypeSymbol candidate, Dictionary? unmanagedTypes) + { + if (!candidate.IsUnmanagedType) + { + unmanagedTypes?[candidate] = false; + return false; + } + + unmanagedTypes = new Dictionary(SymbolEqualityComparer.Default); + + if (unmanagedTypes.TryGetValue(candidate, out var memoizedValue)) + { + if (memoizedValue is { } memoizedResult) + { + return memoizedResult; + } + + return true; + } + + var typeAttributes = candidate.GetAttributes(); + foreach (var attribute in typeAttributes) + { + if (!attribute.IsUnionTypeAttribute()) + { + continue; + } + + foreach (var variant in attribute.AttributeClass?.TypeArguments ?? []) + { + if (isUnmanaged(variant, unmanagedTypes)) + { + continue; + } + + unmanagedTypes[candidate] = false; + return false; + } + } + + if (candidate is INamedTypeSymbol namedCandidate) + { + foreach (var typeParameter in namedCandidate.TypeParameters) + { + if (typeParameter.HasUnmanagedTypeConstraint) + { + continue; + } + + var typeParameterAttributes = typeParameter.GetAttributes(); + + foreach (var attribute in typeParameterAttributes) + { + if (attribute.IsUnionTypeAttribute()) + { + unmanagedTypes[candidate] = false; + return false; + } + } + } + } + + unmanagedTypes[candidate] = true; + return true; + } + } + } + } +} diff --git a/Janus.Tests.EndToEnd/UnionTypeVariantTest.cs b/Janus.Tests.EndToEnd/UnionTypeVariantTest.cs new file mode 100644 index 0000000..f263b89 --- /dev/null +++ b/Janus.Tests.EndToEnd/UnionTypeVariantTest.cs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MPL-2.0 + +namespace RhoMicro.CodeAnalysis.Janus.EndToEnd.Tests; + +public partial class UnionTypeVariantTests +{ + [UnionType] + partial struct Text; + + [UnionType] + partial struct Union; + + [Fact] + public void ManagedUnionTypeStructVariantDoesNotThrowTle() + { + Text text = "foo"; + Union union = text; + Assert.Equal(text, union.CastToText); + } +}