Skip to content

Commit

Permalink
Strengthen the resolver's ability to reference formatters wherever th…
Browse files Browse the repository at this point in the history
…ey are
  • Loading branch information
AArnott committed Apr 22, 2024
1 parent 5fd82a9 commit f9ad05e
Show file tree
Hide file tree
Showing 183 changed files with 964 additions and 1,028 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ namespace MessagePack.SourceGenerator.CodeAnalysis;

public record CustomFormatterRegisterInfo : ResolverRegisterInfo
{
public override string GetFormatterNameForResolver(GenericParameterStyle style) => this.Formatter.GetQualifiedName(Qualifiers.GlobalNamespace, style);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public sealed record EnumSerializationInfo : ResolverRegisterInfo
_ => this.UnderlyingTypeName,
};

public static EnumSerializationInfo Create(INamedTypeSymbol dataType, ISymbol enumUnderlyingType)
public static EnumSerializationInfo Create(INamedTypeSymbol dataType, ISymbol enumUnderlyingType, ResolverOptions resolverOptions)
{
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType);
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType, resolverOptions);
return new EnumSerializationInfo
{
DataType = basicInfo.DataType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public sealed record GenericSerializationInfo : ResolverRegisterInfo
{
public override bool IsUnboundGenericType => false;

public static new GenericSerializationInfo Create(ITypeSymbol dataType)
public static new GenericSerializationInfo Create(ITypeSymbol dataType, ResolverOptions resolverOptions)
{
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType);
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType, resolverOptions);
ImmutableArray<string> typeArguments = CodeAnalysisUtilities.GetTypeArguments(dataType);
return new GenericSerializationInfo
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ public int MaxKey
MemberSerializationInfo[] members,
bool hasIMessagePackSerializationCallbackReceiver,
bool needsCastOnAfter,
bool needsCastOnBefore)
bool needsCastOnBefore,
ResolverOptions resolverOptions)
{
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType);
ResolverRegisterInfo basicInfo = ResolverRegisterInfo.Create(dataType, resolverOptions);
return new ObjectSerializationInfo
{
DataType = basicInfo.DataType,
Expand Down
168 changes: 122 additions & 46 deletions src/MessagePack.SourceGenerator/CodeAnalysis/QualifiedTypeName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace MessagePack.SourceGenerator.CodeAnalysis;

Expand All @@ -12,64 +13,113 @@ namespace MessagePack.SourceGenerator.CodeAnalysis;
/// </summary>
public record QualifiedTypeName : IComparable<QualifiedTypeName>
{
private QualifiedTypeName? nestingType;
private string? @namespace;

/// <summary>
/// Initializes a new instance of the <see cref="QualifiedTypeName"/> class.
/// </summary>
/// <param name="symbol">The symbol this instance will identify.</param>
public QualifiedTypeName(ITypeSymbol symbol)
{
this.Kind = symbol.TypeKind;
if (symbol is IArrayTypeSymbol arrayType)
{
this.ArrayRank = arrayType.Rank;
this.Namespace = arrayType.ElementType.ContainingNamespace.GetFullNamespaceName();
this.Name = arrayType.ElementType.Name;
this.NestingTypes = GetNestingTypes(arrayType.ElementType);
}
else
{
this.Namespace = symbol.ContainingNamespace.GetFullNamespaceName();
this.Name = symbol.Name;
this.NestingTypes = GetNestingTypes(symbol);
if (symbol.ContainingType is not null)
{
this.NestingType = new(symbol.ContainingType);
}
else
{
this.Namespace = symbol.ContainingNamespace.GetFullNamespaceName();
}
}

this.TypeParameters = CodeAnalysisUtilities.GetTypeParameters(symbol);
}

/// <inheritdoc cref="QualifiedTypeName(string?, string?, string, ImmutableArray{string})"/>
public QualifiedTypeName(string? @namespace, string name, ImmutableArray<string>? typeParameters = default)
: this(@namespace, nestingTypes: null, name, typeParameters ?? ImmutableArray<string>.Empty)
/// <inheritdoc cref="QualifiedTypeName(string?, QualifiedTypeName?, TypeKind, string, ImmutableArray{string})"/>
public QualifiedTypeName(string? @namespace, TypeKind kind, string name, ImmutableArray<string>? typeParameters = default)
: this(@namespace, nestingType: null, kind, name, typeParameters ?? ImmutableArray<string>.Empty)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="QualifiedTypeName"/> class.
/// </summary>
/// <param name="namespace">The full namespace of the type. Must not be empty, but may be <see langword="null" />.</param>
/// <param name="nestingTypes">All the nesting types that contain the target type.</param>
/// <param name="nestingType">The type that contains this one, if any.</param>
/// <param name="kind">The C# keyword that identifies the kind of the type (e.g. "class", "struct").</param>
/// <param name="name">The simple name of the type.</param>
/// <param name="typeParameters">The generic type identifiers.</param>
public QualifiedTypeName(string? @namespace, string? nestingTypes, string name, ImmutableArray<string> typeParameters)
public QualifiedTypeName(string? @namespace, QualifiedTypeName? nestingType, TypeKind kind, string name, ImmutableArray<string> typeParameters)
{
if (@namespace == string.Empty)
{
throw new ArgumentException("May be null but not empty.", nameof(@namespace));
}

if (nestingTypes == string.Empty)
if (@namespace is not null && nestingType is not null)
{
throw new ArgumentException("May be null but not empty.", nameof(nestingTypes));
throw new ArgumentException("A type cannot have both a namespace and a containing type.", nameof(@namespace));
}

this.Namespace = @namespace;
this.NestingTypes = nestingTypes;
this.NestingType = nestingType;
this.Kind = kind;
this.Name = name;
this.TypeParameters = typeParameters;
}

public string? Namespace { get; init; }
public string? Namespace
{
get => this.@namespace;
init
{
if (value is not null && this.NestingType is not null)
{
throw new InvalidOperationException("Namespace cannot be specified while NestingType is not null.");
}

@namespace = value;
}
}

public QualifiedTypeName? NestingType
{
get => this.nestingType;
init
{
if (value is not null && this.Namespace is not null)
{
throw new InvalidOperationException("Nesting type cannot be specified while Namespace is not null.");
}

public string? NestingTypes { get; init; }
nestingType = value;
}
}

/// <summary>
/// Gets the kind of type.
/// </summary>
public TypeKind Kind { get; init; }

/// <summary>
/// Gets the access modifier that should accompany any declaration of this type, if any.
/// </summary>
public Accessibility? AccessModifier { get; init; }

/// <summary>
/// Gets the simple name of the type (unqualified by namespace or containing types),
/// and without any generic type parameters.
/// </summary>
public string Name { get; init; }

public ImmutableArray<string> TypeParameters { get; init; }
Expand All @@ -84,21 +134,34 @@ public QualifiedTypeName(string? @namespace, string? nestingTypes, string name,
public string GetQualifiedName(Qualifiers qualifier = Qualifiers.GlobalNamespace, GenericParameterStyle genericStyle = GenericParameterStyle.Identifiers)
{
StringBuilder builder = new();
if (qualifier is Qualifiers.GlobalNamespace)

// No type should have both a namespace and a containing type.
if (this.NestingType is not null && this.Namespace is not null)
{
builder.Append("global::");
throw new InvalidOperationException("No type has both a namespace and containing type.");
}

if (this.Namespace is not null && qualifier >= Qualifiers.Namespace)
if (this.NestingType is not null)
{
builder.Append(this.Namespace);
builder.Append('.');
if (qualifier >= Qualifiers.ContainingTypes)
{
builder.Append(this.NestingType.GetQualifiedName(qualifier, genericStyle));
builder.Append('.');
}
}

if (this.NestingTypes is not null && qualifier >= Qualifiers.ContainingTypes)
else
{
builder.Append(this.NestingTypes);
builder.Append('.');
// Only add the global:: prefix if an alias prefix is not already present.
if (qualifier is Qualifiers.GlobalNamespace && this.Namespace?.Contains("::") is not true)
{
builder.Append("global::");
}

if (this.Namespace is not null && qualifier >= Qualifiers.Namespace)
{
builder.Append(this.Namespace);
builder.Append('.');
}
}

builder.Append(this.Name);
Expand Down Expand Up @@ -136,56 +199,69 @@ public string GetTypeParameters(GenericParameterStyle style)
};
}

private static string? GetNestingTypes(ITypeSymbol symbol)
public int CompareTo(QualifiedTypeName other) => Compare(this, other);

public virtual bool Equals(QualifiedTypeName? other)
{
string? nestingTypes = null;
INamedTypeSymbol? nestingType = symbol.ContainingType;
while (nestingType is not null)
if (other is null)
{
nestingTypes = nestingTypes is null ? nestingType.Name : $"{nestingType.Name}.{nestingTypes}";
nestingType = nestingType.ContainingType;
return false;
}

return nestingTypes;
return this.Namespace == other.Namespace &&
this.NestingType == other.NestingType &&
this.Kind == other.Kind &&
this.Name == other.Name &&
this.TypeParameters.SequenceEqual(other.TypeParameters);
}

public int CompareTo(QualifiedTypeName other)
public override int GetHashCode() => this.Name.GetHashCode();

public override string ToString() => this.GetQualifiedName(Qualifiers.Namespace);

private static int Compare(QualifiedTypeName? left, QualifiedTypeName? right)
{
int result = StringComparer.Ordinal.Compare(this.Namespace, other.Namespace);
if (left is null && right is null)
{
return 0;
}

if (left is null)
{
return -1;
}

if (right is null)
{
return 1;
}

int result = StringComparer.Ordinal.Compare(left.Namespace, right.Namespace);
if (result != 0)
{
return result;
}

result = StringComparer.Ordinal.Compare(this.Name, other.Name);
result = StringComparer.Ordinal.Compare(left.Kind, right.Kind);
if (result != 0)
{
return result;
}

result = StringComparer.Ordinal.Compare(this.NestingTypes, other.NestingTypes);
result = StringComparer.Ordinal.Compare(left.Name, right.Name);
if (result != 0)
{
return result;
}

return StringComparer.Ordinal.Compare(this.Name, other.Name);
}

public virtual bool Equals(QualifiedTypeName? other)
{
if (other is null)
result = Compare(left.NestingType, right.NestingType);
if (result != 0)
{
return false;
return result;
}

return this.Namespace == other.Namespace &&
this.NestingTypes == other.NestingTypes &&
this.Name == other.Name &&
this.TypeParameters.SequenceEqual(other.TypeParameters);
return StringComparer.Ordinal.Compare(left.Name, right.Name);
}

public override int GetHashCode() => this.Name.GetHashCode();
}

public enum GenericParameterStyle
Expand Down

0 comments on commit f9ad05e

Please sign in to comment.