diff --git a/Directory.Build.props b/Directory.Build.props
index f6ff596..b7ffbb3 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -9,7 +9,7 @@
Choice generator
ALTA Software llc.
Copyright © 2024 ALTA Software llc.
- 1.3.3
+ 1.3.4
diff --git a/src/AltaSoft.Choice.Generator/Executor.cs b/src/AltaSoft.Choice.Generator/Executor.cs
index 06bafaa..354c734 100644
--- a/src/AltaSoft.Choice.Generator/Executor.cs
+++ b/src/AltaSoft.Choice.Generator/Executor.cs
@@ -151,10 +151,8 @@ private static SourceCodeBuilder Process(INamedTypeSymbol typeSymbol, List instance " +
- $"and sets its value using the specified .");
+ sb.AppendSummary($"Creates a new instance and sets its value using the specified {prop.TypeSymbol.GetCrefForType()}.");
sb.AppendParamDescription("value", "The value to assign to the created choice instance.");
sb.Append($"public static {typeFullName} CreateAs").Append(prop.Name).Append("(")
@@ -231,8 +229,8 @@ private static bool ProcessImplicitOperators(SourceCodeBuilder sb, string typeNa
foreach (var property in processedProperties)
{
- sb.AppendSummary($"Implicitly converts an to an .");
- sb.AppendParamDescription("value", $"The to convert.");
+ sb.AppendSummary($"Implicitly converts an {property.TypeSymbol.GetCrefForType()} to an .");
+ sb.AppendParamDescription("value", $"The {property.TypeSymbol.GetCrefForType()} to convert.");
sb.AppendBlock("returns", $" instance representing the code.").NewLine();
sb.AppendLine("[return: NotNullIfNotNull(parameterName: nameof(value))]");
diff --git a/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs b/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs
index dd19b24..e263bbd 100644
--- a/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs
+++ b/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs
@@ -201,6 +201,26 @@ public static bool IsNullableValueType(this INamedTypeSymbol type, out ITypeSymb
return false;
}
+ ///
+ /// Builds a short XML documentation fragment that references the provided using a cref tag.
+ /// For array types the fragment references the element type and appends the word "array." to indicate the collection nature
+ /// (for example: <see cref="System.String"/> array.). For non-array types the fragment is simply the cref reference followed by a period.
+ ///
+ /// The Roslyn to create a cref for. This should not be null.
+ ///
+ /// A string containing an XML <see cref="System.Object" /> tag that references the type. Example results:
+ /// - For a type: "<see cref="Namespace.TypeName"/>."
+ /// - For an array: "<see cref="Namespace.ElementType"/> array."
+ ///
+ public static string GetCrefForType(this ITypeSymbol type)
+ {
+ if (type is IArrayTypeSymbol array)
+ {
+ return $" array";
+ }
+ return $"";
+ }
+
///
/// A dictionary that provides aliases for common .NET framework types, mapping their full names to shorter aliases.
///
diff --git a/src/AltaSoft.Choice.Generator/Helpers/Constants.cs b/src/AltaSoft.Choice.Generator/Helpers/Constants.cs
index c9269bb..f9c491d 100644
--- a/src/AltaSoft.Choice.Generator/Helpers/Constants.cs
+++ b/src/AltaSoft.Choice.Generator/Helpers/Constants.cs
@@ -16,10 +16,4 @@ internal static class Constants
/// This constant is used to identify the XmlTagAttribute type by its name.
///
internal const string XmlTagAttributeFullName = "AltaSoft.Choice.XmlTagAttribute";
-
- ///
- /// The fully qualified name of the IXmlSerializable interface.
- /// This constant is used to identify the IXmlSerializable type by its name.
- ///
- internal const string IXmlSerializableFullName = "IXmlSerializable";
}
diff --git a/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs b/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs
index d7e075d..1f42306 100644
--- a/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs
+++ b/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs
@@ -43,14 +43,14 @@ internal sealed class PropertyDetails
///
/// The type symbol of the property.
///
- internal ITypeSymbol TypeSymbol { get; private set; }
+ internal ITypeSymbol TypeSymbol { get; }
///
/// returns if the type is dateOnly
///
internal bool IsDateOnly()
{
- return TypeSymbol.IsValueType && TypeSymbol.GetFullName()?.Replace("?", "") == "System.DateOnly";
+ return TypeSymbol.IsValueType && TypeSymbol.GetFullName().Replace("?", "") == "System.DateOnly";
}
///
/// Initializes a new instance of the class.
diff --git a/tests/AltaSoft.Choice.Generator.SnapshotTests/ChoiceGeneratorTest.cs b/tests/AltaSoft.Choice.Generator.SnapshotTests/ChoiceGeneratorTest.cs
index d3c6916..7402982 100644
--- a/tests/AltaSoft.Choice.Generator.SnapshotTests/ChoiceGeneratorTest.cs
+++ b/tests/AltaSoft.Choice.Generator.SnapshotTests/ChoiceGeneratorTest.cs
@@ -64,6 +64,37 @@ public enum Authorisation1Code
Assert.Single(x);
});
}
+ [Fact]
+ public Task ChoiceTypeShouldGenerateDocumentationCorrectly_ForArrayInChoice()
+ {
+ const string source =
+ """
+ using System;
+ using System.Xml;
+ using System.Xml.Schema;
+ using System.Xml.Serialization;
+ using AltaSoft.Choice;
+
+ namespace TestNamespace
+ {
+ [Choice]
+ public sealed partial class ArrayInTypeChoice
+ {
+ public partial string? StringChoice { get; set; }
+
+ public partial AccountId[]? Accounts { get; set; }
+ }
+
+ public sealed record AccountId(int Id);
+ }
+
+ """;
+
+ return TestHelper.Verify(source, (_, x, _) =>
+ {
+ Assert.Single(x);
+ });
+ }
[Fact]
public Task ChoiceTypeShouldNotGenerateImplicitMethodsAndCompileCorrectly()
@@ -120,7 +151,7 @@ public static class TestHelper
internal static Task Verify(string source, Action, List, GeneratorDriver>? additionalChecks = null)
{
List assemblies = [typeof(XmlElementAttribute).Assembly, typeof(JsonSerializer).Assembly];
- var (diagnostics, output, driver) = TestHelpers.GetGeneratedOutput(source, assemblies);
+ var (diagnostics, output, driver) = TestHelpers.GetGeneratedOutput(source, assemblies);
Assert.Empty(diagnostics.Where(x => x.Severity == DiagnosticSeverity.Error));
additionalChecks?.Invoke(diagnostics, output, driver);
diff --git a/tests/AltaSoft.Choice.Generator.SnapshotTests/ModuleInitializer.cs b/tests/AltaSoft.Choice.Generator.SnapshotTests/ModuleInitializer.cs
index addf7ce..25b9830 100644
--- a/tests/AltaSoft.Choice.Generator.SnapshotTests/ModuleInitializer.cs
+++ b/tests/AltaSoft.Choice.Generator.SnapshotTests/ModuleInitializer.cs
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
+using DiffEngine;
using VerifyTests;
namespace AltaSoft.Choice.Generator.SnapshotTests;
@@ -8,6 +9,7 @@ public static class ModuleInitializer
[ModuleInitializer]
public static void Init()
{
+ DiffTools.UseOrder(DiffTool.VisualStudioCode, DiffTool.VisualStudio, DiffTool.AraxisMerge, DiffTool.BeyondCompare);
VerifySourceGenerators.Initialize();
}
}
diff --git a/tests/AltaSoft.Choice.Generator.SnapshotTests/Snapshots/ChoiceGeneratorTest.ChoiceTypeShouldGenerateDocumentationCorrectly_ForArrayInChoice#ArrayInTypeChoice.g.verified.cs b/tests/AltaSoft.Choice.Generator.SnapshotTests/Snapshots/ChoiceGeneratorTest.ChoiceTypeShouldGenerateDocumentationCorrectly_ForArrayInChoice#ArrayInTypeChoice.g.verified.cs
new file mode 100644
index 0000000..654e6f1
--- /dev/null
+++ b/tests/AltaSoft.Choice.Generator.SnapshotTests/Snapshots/ChoiceGeneratorTest.ChoiceTypeShouldGenerateDocumentationCorrectly_ForArrayInChoice#ArrayInTypeChoice.g.verified.cs
@@ -0,0 +1,170 @@
+//HintName: ArrayInTypeChoice.g.cs
+//------------------------------------------------------------------------------
+//
+// This code was generated by 'AltaSoft Choice.Generator'.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+#nullable enable
+
+using TestNamespace;
+using AltaSoft.Choice;
+using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Xml;
+using System.Xml.Serialization;
+using System.Xml.Schema;
+
+namespace TestNamespace;
+
+#pragma warning disable CS8774 // Member must have a non-null value when exiting.
+#pragma warning disable CS0628 // New protected member declared in sealed type
+
+public sealed partial class ArrayInTypeChoice
+{
+ ///
+ /// Constructor for Serialization/Deserialization
+ ///
+ [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+ public ArrayInTypeChoice()
+ {
+ }
+
+ ///
+ /// Choice enum
+ ///
+ [JsonIgnore]
+ [XmlIgnore]
+ [ChoiceTypeProperty]
+ public ChoiceOf ChoiceType { get; private set; }
+
+ private string? _stringChoice;
+
+ [DisallowNull]
+ [XmlElement("StringChoice")]
+ [ChoiceProperty]
+ public partial string? StringChoice
+ {
+ get => _stringChoice;
+ set
+ {
+ _stringChoice = value ?? throw new InvalidOperationException("Choice value cannot be null");
+ _accounts = null;
+ ChoiceType = ChoiceOf.StringChoice;
+ }
+ }
+
+ private TestNamespace.AccountId[]? _accounts;
+
+ [DisallowNull]
+ [XmlElement("Accounts")]
+ [ChoiceProperty]
+ public partial TestNamespace.AccountId[]? Accounts
+ {
+ get => _accounts;
+ set
+ {
+ _accounts = value ?? throw new InvalidOperationException("Choice value cannot be null");
+ _stringChoice = null;
+ ChoiceType = ChoiceOf.Accounts;
+ }
+ }
+
+
+ ///
+ /// Creates a new instance and sets its value using the specified .
+ ///
+ /// The value to assign to the created choice instance.
+ public static TestNamespace.ArrayInTypeChoice CreateAsStringChoice(string value) => new () { StringChoice = value };
+
+ ///
+ /// Creates a new instance and sets its value using the specified array.
+ ///
+ /// The value to assign to the created choice instance.
+ public static TestNamespace.ArrayInTypeChoice CreateAsAccounts(TestNamespace.AccountId[] value) => new () { Accounts = value };
+
+ ///
+ /// Applies the appropriate function based on the current choice type
+ ///
+ /// The return type of the provided match functions
+ /// Function to invoke if the choice is a value
+ /// Function to invoke if the choice is a value
+ public TResult Match(
+ Func matchStringChoice,
+ Func matchAccounts)
+ {
+ return ChoiceType switch
+ {
+ ChoiceOf.StringChoice => matchStringChoice(StringChoice!),
+ ChoiceOf.Accounts => matchAccounts(Accounts!),
+ _ => throw new InvalidOperationException($"Invalid ChoiceType. '{ChoiceType}'")
+ };
+ }
+
+ ///
+ /// Applies the appropriate Action based on the current choice type
+ ///
+ /// Action to invoke if the choice is a value
+ /// Action to invoke if the choice is a value
+ public void Switch(
+ Action matchStringChoice,
+ Action matchAccounts)
+ {
+ switch (ChoiceType)
+ {
+ case ChoiceOf.StringChoice:
+ matchStringChoice(StringChoice!);
+ return;
+
+ case ChoiceOf.Accounts:
+ matchAccounts(Accounts!);
+ return;
+
+ default:
+ throw new XmlException($"Invalid ChoiceType. '{ChoiceType}'");
+ }
+ }
+
+ ///
+ /// Implicitly converts an to an .
+ ///
+ /// The to convert.
+ ///
+ /// instance representing the code.
+ ///
+
+ [return: NotNullIfNotNull(parameterName: nameof(value))]
+ public static implicit operator ArrayInTypeChoice? (string? value)
+ {
+ return value is null ? null : CreateAsStringChoice(value);
+ }
+
+ ///
+ /// Implicitly converts an array to an .
+ ///
+ /// The array to convert.
+ ///
+ /// instance representing the code.
+ ///
+
+ [return: NotNullIfNotNull(parameterName: nameof(value))]
+ public static implicit operator ArrayInTypeChoice? (TestNamespace.AccountId[]? value)
+ {
+ return value is null ? null : CreateAsAccounts(value);
+ }
+
+ ///
+ /// Choice enumeration
+ ///
+ [XmlType("ChoiceOf.ArrayInTypeChoice")]
+ public enum ChoiceOf
+ {
+ StringChoice,
+ Accounts,
+ }
+}
diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/ArrayInTypeChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/ArrayInTypeChoice.cs
new file mode 100644
index 0000000..dd0f168
--- /dev/null
+++ b/tests/AltaSoft.ChoiceGenerator.Tests/ArrayInTypeChoice.cs
@@ -0,0 +1,13 @@
+using AltaSoft.Choice;
+
+namespace AltaSoft.ChoiceGenerator.Tests;
+
+[Choice]
+public sealed partial class ArrayInTypeChoice
+{
+ public partial string? StringChoice { get; set; }
+
+ public partial AccountId[]? Accounts { get; set; }
+}
+
+public sealed record AccountId(int Id);