Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions DomainModeling.Generator/NamespaceSymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis;

namespace Architect.DomainModeling.Generator;

Expand All @@ -12,25 +12,28 @@ internal static class NamespaceSymbolExtensions
/// </summary>
public static bool IsInSystemNamespace(this INamespaceSymbol namespaceSymbol)
{
while (namespaceSymbol.ContainingNamespace is not null)
while (namespaceSymbol?.ContainingNamespace is not null)
namespaceSymbol = namespaceSymbol.ContainingNamespace;

return namespaceSymbol.Name == "System";
return namespaceSymbol?.Name == "System";
}

/// <summary>
/// Returns whether the given <see cref="INamedTypeSymbol"/> has the given <paramref name="fullName"/>.
/// </summary>
public static bool HasFullName(this INamespaceSymbol namespaceSymbol, string fullName)
public static bool HasFullName(this INamespaceSymbol? namespaceSymbol, string fullName)
{
return namespaceSymbol.HasFullName(fullName.AsSpan());
}

/// <summary>
/// Returns whether the given <see cref="INamedTypeSymbol"/> has the given <paramref name="fullName"/>.
/// </summary>
public static bool HasFullName(this INamespaceSymbol namespaceSymbol, ReadOnlySpan<char> fullName)
public static bool HasFullName(this INamespaceSymbol? namespaceSymbol, ReadOnlySpan<char> fullName)
{
if (namespaceSymbol is null)
return false;

do
{
var length = namespaceSymbol.Name.Length;
Expand Down
7 changes: 6 additions & 1 deletion DomainModeling.Generator/TypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static bool IsType(this ITypeSymbol typeSymbol, ITypeSymbol comparand)

chars = chars.Slice(0, chars.Length - freeBuffer.Length);
if (containingNamespace?.IsGlobalNamespace != false)
chars = typeSymbol.ContainingNamespace.ToString().AsSpan();
chars = (typeSymbol.ContainingNamespace?.ToString() ?? "").AsSpan();

if (!typeSymbol.IsType(typeSymbol.Name.AsSpan(), chars))
return false;
Expand Down Expand Up @@ -333,6 +333,11 @@ public static bool IsEnumerable(this ITypeSymbol typeSymbol, out INamedTypeSymbo
if (!typeSymbol.IsOrImplementsInterface(type => type.IsType("IEnumerable", "System.Collections", generic: false), out var nonGenericEnumerableInterface))
return false;

if (typeSymbol.Kind == SymbolKind.ArrayType)
{
elementType = ((IArrayTypeSymbol)typeSymbol).ElementType as INamedTypeSymbol;
return true;
}
if (typeSymbol.IsOrImplementsInterface(type => type.IsType("IList", "System.Collections.Generic", generic: true), out var interf))
{
elementType = interf.TypeArguments[0] as INamedTypeSymbol;
Expand Down
6 changes: 3 additions & 3 deletions DomainModeling.Generator/ValueObjectGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ private static bool FilterSyntaxNode(SyntaxNode node, CancellationToken cancella
{
dataMemberHashCode = tuple.Member.Name.GetStableHashCode64(dataMemberHashCode);
dataMemberHashCode = ":".GetStableHashCode64(dataMemberHashCode);
dataMemberHashCode = tuple.Type.ContainingNamespace.ToString().GetStableHashCode64(dataMemberHashCode);
dataMemberHashCode = (tuple.Type.ContainingNamespace?.ToString() ?? "").GetStableHashCode64(dataMemberHashCode); // Arrays have no namespace
dataMemberHashCode = ".".GetStableHashCode64(dataMemberHashCode);
dataMemberHashCode = tuple.Type.Name.GetStableHashCode64(dataMemberHashCode);
dataMemberHashCode = "&".GetStableHashCode64(dataMemberHashCode);
Expand Down Expand Up @@ -288,7 +288,7 @@ public bool Equals({typeName}? other)
{(existingComponents.HasFlags(ValueObjectTypeComponents.EqualsMethod) ? " */" : "")}

/// <summary>
/// Provides type inference when comparing types that are completed source-generated. The current code's source generator does not know the appropriate namespace, because the type is being generated at the same time, thus necessitating type inference.
/// Provides type inference when comparing types that are entirely source-generated. The current code's source generator does not know the appropriate namespace, because the type is being generated at the same time, thus necessitating type inference.
/// </summary>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static bool Equals<T>(T left, T right)
Expand All @@ -307,7 +307,7 @@ public int CompareTo({typeName}? other)
}}

/// <summary>
/// Provides type inference when comparing types that are completed source-generated. The current code's source generator does not know the appropriate namespace, because the type is being generated at the same time, thus necessitating type inference.
/// Provides type inference when comparing types that are entirely source-generated. The current code's source generator does not know the appropriate namespace, because the type is being generated at the same time, thus necessitating type inference.
/// </summary>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static int Compare<T>(T left, T right)
Expand Down
19 changes: 19 additions & 0 deletions DomainModeling.Tests/ValueObjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,25 @@ public ImmutableArrayValueObject(IEnumerable<string> values)
}
}

/// <summary>
/// Should merely compile.
/// </summary>
[Obsolete("Should merely compile.", error: true)]
[SourceGenerated]
public sealed partial class ArrayValueObject : ValueObject
{
protected override StringComparison StringComparison => StringComparison.OrdinalIgnoreCase;

public string?[]? StringValues { get; }
public int?[] IntValues { get; }

public ArrayValueObject(string?[]? stringValues, int?[] intValues)
{
this.StringValues = stringValues;
this.IntValues = intValues;
}
}

[SourceGenerated]
public sealed partial class CustomCollectionValueObject : ValueObject
{
Expand Down
12 changes: 12 additions & 0 deletions DomainModeling.Tests/WrapperValueObjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,18 @@ public CustomCollection(string value)
}
}
[SourceGenerated]
[Obsolete("Should merely compile.", error: true)]
public sealed partial class StringArrayValue : WrapperValueObject<string?[]>
{
}
[SourceGenerated]
[Obsolete("Should merely compile.", error: true)]
public sealed partial class DecimalArrayValue : WrapperValueObject<decimal?[]>
{
}

/// <summary>
/// Should merely compile.
/// </summary>
Expand Down
5 changes: 4 additions & 1 deletion DomainModeling/DomainModeling.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@
</PropertyGroup>

<PropertyGroup>
<VersionPrefix>2.0.0</VersionPrefix>
<VersionPrefix>2.0.1</VersionPrefix>
<Description>
A complete Domain-Driven Design (DDD) toolset for implementing domain models, including base types and source generators.

https://github.com/TheArchitectDev/Architect.DomainModeling

Release notes:

2.0.1:
- Fixed a bug where arrays in (Wrapper)ValueObjects would trip the generator.

2.0.0:
- BREAKING: Generated DummyBuilders now use UTC datetimes for generated defaults and for interpreting datetime strings.
- Semi-breaking: Generated types no longer add [Serializable] attribute, since there would be no way to remove it.
Expand Down