This commit is contained in:
Wvader 2021-12-11 21:06:58 +00:00
parent 768dbb4532
commit efdd3ada94
11 changed files with 152 additions and 52 deletions

View File

@ -11,26 +11,25 @@ namespace MapTo
internal ClassMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
: base(compilation, sourceGenerationOptions, typeSyntax) { }
protected override ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
protected override ImmutableArray<MappedProperty> GetSourceMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
{
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
var sourceProperties = sourceTypeSymbol.GetAllMembers().ToArray();
return typeSymbol
.GetAllMembers(!isInheritFromMappedBaseClass)
.OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol))
.Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!;
}
protected override ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
protected override ImmutableArray<MappedProperty> GetTypeMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
{
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
var sourceProperties = sourceTypeSymbol.GetAllMembers().ToArray();
return typeSymbol
.GetAllMembers()
.OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol))
.Select(property => MapProperty(typeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null)

View File

@ -27,8 +27,12 @@ namespace MapTo
internal static Diagnostic NoMatchingPropertyTypeFoundError(ISymbol property) =>
Create($"{ErrorId}031", property.Locations.FirstOrDefault(), $"Cannot create a map for '{property.ToDisplayString()}' property because source and destination types are not implicitly convertible. Consider using '{MapTypeConverterAttributeSource.FullyQualifiedName}' to provide a type converter or ignore the property using '{IgnorePropertyAttributeSource.FullyQualifiedName}'.");
internal static Diagnostic InvalidTypeConverterGenericTypesError(ISymbol property, IPropertySymbol sourceProperty) =>
Create($"{ErrorId}032", property.Locations.FirstOrDefault(), $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{ITypeConverterSource.InterfaceName}<{sourceProperty.Type.ToDisplayString()}, {property.GetTypeSymbol()?.ToDisplayString()}>'.");
internal static Diagnostic InvalidTypeConverterGenericTypesError(ISymbol property, ISymbol memberSymbol)
{
return Create($"{ErrorId}032", property.Locations.FirstOrDefault(), $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{ITypeConverterSource.InterfaceName}<{memberSymbol.ToString()}, {property.GetTypeSymbol()?.ToDisplayString()}>'.");
}
internal static Diagnostic ConfigurationParseError(string error) =>
Create($"{ErrorId}040", Location.None, error);

View File

@ -88,7 +88,7 @@ namespace MapTo.Extensions
return builder.WriteClosingBracket();
}
private static bool IsMappedProperty(this System.Collections.Immutable.ImmutableArray<MappedProperty> properties, MappedProperty property) {
private static bool IsMappedProperty(this ImmutableArray<MappedProperty> properties, MappedProperty property) {
foreach(var prop in properties)
{
@ -98,7 +98,7 @@ namespace MapTo.Extensions
return false;
}
private static SourceBuilder TryWriteProperties(this SourceBuilder builder, System.Collections.Immutable.ImmutableArray<MappedProperty> properties, System.Collections.Immutable.ImmutableArray<MappedProperty>? otherProperties,
private static SourceBuilder TryWriteProperties(this SourceBuilder builder, ImmutableArray<MappedProperty> properties, System.Collections.Immutable.ImmutableArray<MappedProperty>? otherProperties,
string? sourceClassParameterName, string mappingContextParameterName, bool fromUpdate)
{
if (fromUpdate)

View File

@ -67,6 +67,9 @@ namespace MapTo.Extensions
case IPropertySymbol propertySymbol:
typeSymbol = propertySymbol.Type;
return true;
case IFieldSymbol fieldSymbol:
typeSymbol = fieldSymbol.Type;
return true;
case IParameterSymbol parameterSymbol:
typeSymbol = parameterSymbol.Type;

View File

@ -34,6 +34,8 @@ namespace MapTo
MapPropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapPropertyAttributeSource.FullyQualifiedName);
MapFromAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapFromAttributeSource.FullyQualifiedName);
UseUpdateAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(UseUpdateAttributeSource.FullyQualifiedName);
JsonExtensionAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(UseUpdateAttributeSource.FullyQualifiedName);
MappingContextTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MappingContextSource.FullyQualifiedName);
AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis");
@ -51,6 +53,8 @@ namespace MapTo
protected INamedTypeSymbol UseUpdateAttributeTypeSymbol { get; }
protected INamedTypeSymbol JsonExtensionAttributeTypeSymbol { get; }
protected INamedTypeSymbol MappingContextTypeSymbol { get; }
protected INamedTypeSymbol MapPropertyAttributeTypeSymbol { get; }
@ -99,19 +103,31 @@ namespace MapTo
}
}
protected IPropertySymbol? FindSourceProperty(IEnumerable<IPropertySymbol> sourceProperties, ISymbol property)
protected ISymbol? FindSourceProperty(IEnumerable<ISymbol> sourceMembers, ISymbol member)
{
var propertyName = property
var propertyName = member
.GetAttribute(MapPropertyAttributeTypeSymbol)
?.NamedArguments
.SingleOrDefault(a => a.Key == MapPropertyAttributeSource.SourcePropertyNamePropertyName)
.Value.Value as string ?? property.Name;
.Value.Value as string ?? member.Name;
foreach(var sourceProperty in sourceMembers)
{
if(sourceProperty is IPropertySymbol propertySymbol)
{
if (propertySymbol.Name == propertyName) return sourceProperty;
}
if (sourceProperty is IFieldSymbol fieldSymbol)
{
if (fieldSymbol.Name == propertyName) return sourceProperty;
}
return sourceProperties.SingleOrDefault(p => p.Name == propertyName);
}
return sourceMembers.SingleOrDefault(p => p.Name == propertyName);
}
protected abstract ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass);
protected abstract ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass);
protected abstract ImmutableArray<MappedProperty> GetSourceMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass);
protected abstract ImmutableArray<MappedProperty> GetTypeMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass);
protected INamedTypeSymbol? GetSourceTypeSymbol(TypeDeclarationSyntax typeDeclarationSyntax, SemanticModel? semanticModel = null) =>
GetSourceTypeSymbol(typeDeclarationSyntax.GetAttribute(MapFromAttributeSource.AttributeName), semanticModel);
@ -144,7 +160,12 @@ namespace MapTo
return TypeSyntax.GetAttribute("UseUpdate") != null;
}
protected virtual MappedProperty? MapProperty(ISymbol sourceTypeSymbol, IReadOnlyCollection<IPropertySymbol> sourceProperties, ISymbol property)
protected bool IsJsonExtension()
{
return TypeSyntax.GetAttribute("JsonExtension") != null;
}
protected virtual MappedProperty? MapProperty(ISymbol sourceTypeSymbol, IReadOnlyCollection<ISymbol> sourceProperties, ISymbol property)
{
var sourceProperty = FindSourceProperty(sourceProperties, property);
if (sourceProperty is null || !property.TryGetTypeSymbol(out var propertyType))
@ -171,6 +192,16 @@ namespace MapTo
AddUsingIfRequired(enumerableTypeArgumentType);
AddUsingIfRequired(mappedSourcePropertyType);
bool isReadOnly = false;
if(property is IPropertySymbol pSymbol)
{
isReadOnly = pSymbol.IsReadOnly;
}
if (property is IFieldSymbol fSymbol)
{
isReadOnly = fSymbol.IsReadOnly;
}
return new MappedProperty(
property.Name,
@ -181,7 +212,7 @@ namespace MapTo
sourceProperty.Name,
ToQualifiedDisplayName(mappedSourcePropertyType),
ToQualifiedDisplayName(enumerableTypeArgumentType),
(property as IPropertySymbol).IsReadOnly);
isReadOnly);
;
}
protected virtual MappedProperty? MapPropertySimple(ISymbol sourceTypeSymbol, ISymbol property)
@ -202,7 +233,16 @@ namespace MapTo
AddUsingIfRequired(enumerableTypeArgumentType);
AddUsingIfRequired(mappedSourcePropertyType);
bool isReadOnly = false;
if (property is IPropertySymbol pSymbol)
{
isReadOnly = pSymbol.IsReadOnly;
}
if (property is IFieldSymbol fSymbol)
{
isReadOnly = fSymbol.IsReadOnly;
}
return new MappedProperty(
property.Name,
property.GetTypeSymbol().ToString(),
@ -212,10 +252,10 @@ namespace MapTo
property.Name,
ToQualifiedDisplayName(mappedSourcePropertyType),
ToQualifiedDisplayName(enumerableTypeArgumentType),
(property as IPropertySymbol).IsReadOnly);
isReadOnly);
;
}
protected bool TryGetMapTypeConverter(ISymbol property, IPropertySymbol sourceProperty, out string? converterFullyQualifiedName,
protected bool TryGetMapTypeConverter(ISymbol property, ISymbol sourceMember, out string? converterFullyQualifiedName,
out ImmutableArray<string> converterParameters)
{
converterFullyQualifiedName = null;
@ -232,10 +272,10 @@ namespace MapTo
return false;
}
var baseInterface = GetTypeConverterBaseInterface(converterTypeSymbol, property, sourceProperty);
var baseInterface = GetTypeConverterBaseInterface(converterTypeSymbol, property, sourceMember);
if (baseInterface is null)
{
AddDiagnostic(DiagnosticsFactory.InvalidTypeConverterGenericTypesError(property, sourceProperty));
AddDiagnostic(DiagnosticsFactory.InvalidTypeConverterGenericTypesError(property, sourceMember));
return false;
}
@ -310,18 +350,19 @@ namespace MapTo
var sourceTypeIdentifierName = sourceTypeSymbol.Name;
var isTypeInheritFromMappedBaseClass = IsTypeInheritFromMappedBaseClass(semanticModel);
var isTypeUpdatable = IsTypeUpdatable();
var isJsonExtension = IsJsonExtension();
var shouldGenerateSecondaryConstructor = ShouldGenerateSecondaryConstructor(semanticModel, sourceTypeSymbol);
var mappedProperties = GetSourceMappedProperties(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass);
if (!mappedProperties.Any())
var mappedProperties = GetSourceMappedMembers(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass);
/* if (!mappedProperties.Any())
{
AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyFoundError(TypeSyntax.GetLocation(), typeSymbol, sourceTypeSymbol));
return null;
}
}*/
AddUsingIfRequired(mappedProperties.Any(p => p.IsEnumerable), "System.Linq");
var allProperties = GetTypeMappedProperties(sourceTypeSymbol, typeSymbol , isTypeInheritFromMappedBaseClass);
var allProperties = GetTypeMappedMembers(sourceTypeSymbol, typeSymbol , isTypeInheritFromMappedBaseClass);
return new MappingModel(
SourceGenerationOptions,
@ -333,6 +374,7 @@ namespace MapTo
sourceTypeIdentifierName,
sourceTypeSymbol.ToDisplayString(),
isTypeUpdatable,
isJsonExtension,
mappedProperties,
allProperties,
isTypeInheritFromMappedBaseClass,
@ -342,18 +384,31 @@ namespace MapTo
private INamedTypeSymbol? GetTypeConverterBaseInterface(ITypeSymbol converterTypeSymbol, ISymbol property, IPropertySymbol sourceProperty)
private INamedTypeSymbol? GetTypeConverterBaseInterface(ITypeSymbol converterTypeSymbol, ISymbol property, ISymbol sourceMember)
{
if (!property.TryGetTypeSymbol(out var propertyType))
{
return null;
}
ISymbol? type = null;
if(sourceMember is IPropertySymbol propertySymbol)
{
type = propertySymbol.Type;
}
if (sourceMember is IFieldSymbol fieldSymbol)
{
type = fieldSymbol.Type;
}
if (type == null) return null;
return converterTypeSymbol.AllInterfaces
.SingleOrDefault(i =>
i.TypeArguments.Length == 2 &&
SymbolEqualityComparer.Default.Equals(i.ConstructedFrom, TypeConverterInterfaceTypeSymbol) &&
SymbolEqualityComparer.Default.Equals(sourceProperty.Type, i.TypeArguments[0]) &&
SymbolEqualityComparer.Default.Equals(type, i.TypeArguments[0]) &&
SymbolEqualityComparer.Default.Equals(propertyType, i.TypeArguments[1]));
}

View File

@ -46,6 +46,7 @@ namespace MapTo
string SourceTypeIdentifierName,
string SourceTypeFullName,
bool IsTypeUpdatable,
bool IsJsonExtension,
ImmutableArray<MappedProperty> SourceProperties,
ImmutableArray<MappedProperty> TypeProperties,
bool HasMappedBaseClass,

View File

@ -11,7 +11,7 @@ namespace MapTo
internal RecordMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
: base(compilation, sourceGenerationOptions, typeSyntax) { }
protected override ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
protected override ImmutableArray<MappedProperty> GetSourceMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
{
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
return typeSymbol.GetMembers()
@ -25,7 +25,7 @@ namespace MapTo
.ToImmutableArray()!;
}
protected override ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
protected override ImmutableArray<MappedProperty> GetTypeMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass)
{
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
return typeSymbol.GetMembers()

View File

@ -0,0 +1,40 @@
using static MapTo.Sources.Constants;
namespace MapTo.Sources
{
internal static class JsonExtensionAttributeSource
{
internal const string AttributeName = "JsonExtension";
internal const string AttributeClassName = AttributeName + "Attribute";
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
internal static SourceCode Generate(SourceGenerationOptions options)
{
using var builder = new SourceBuilder()
.WriteLine(GeneratedFilesHeader)
.WriteLine("using System;")
.WriteLine()
.WriteLine($"namespace {RootNamespace}")
.WriteOpeningBracket();
if (options.GenerateXmlDocument)
{
builder
.WriteLine("/// <summary>")
.WriteLine("/// Specifies that the annotated needs ToJson method")
.WriteLine("/// </summary>");
}
builder
.WriteLine("[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]")
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
.WriteOpeningBracket();
builder
.WriteClosingBracket() // class
.WriteClosingBracket(); // namespace
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
}
}
}

View File

@ -11,25 +11,23 @@ namespace MapTo
internal StructMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax)
: base(compilation, sourceGenerationOptions, typeSyntax) { }
protected override ImmutableArray<MappedProperty> GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass)
protected override ImmutableArray<MappedProperty> GetSourceMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass)
{
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
return typeSymbol
.GetAllMembers()
.OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol))
.Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!;
}
protected override ImmutableArray<MappedProperty> GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass)
protected override ImmutableArray<MappedProperty> GetTypeMappedMembers(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass)
{
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
return sourceTypeSymbol
.GetAllMembers()
.OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol))
.Select(property => MapPropertySimple(typeSymbol, property))
.Where(mappedProperty => mappedProperty is not null)

View File

@ -8,17 +8,17 @@ namespace BlueWest.Data
[MapFrom(typeof(UserUpdateDto))]
public partial class User
{
public int Id { get; }
public string Name { get; set; }
public string Address { get; set; }
public string BTCAddress { get; set; }
public string LTCAddress { get; set; }
public readonly int Id;
public string Name;
public string Address;
public double BTCAmount { get; set; }
public double LTCAmount { get; set; }
public string BTCAddress;
public string LTCAddress;
public List<FinanceTransaction> FinanceTransactions { get; }
public double BTCAmount;
public double LTCAmount;
public List<FinanceTransaction> FinanceTransactions;
public User(int id, string name, string address, string btcAddress, string ltcAddress, double btcAmount, double ltcAmount, List<FinanceTransaction> financeTransactions)
{

View File

@ -4,16 +4,16 @@ namespace BlueWest.Data
{
[MapFrom(typeof(User))]
public partial struct UserUpdateDto
public partial class UserUpdateDto
{
public string Name { get; set; }
public string Address { get; set; }
public string BTCAddress { get; set; }
public string LTCAddress { get; set; }
public string Name;
public string Address;
public double BTCAmount { get; set; }
public double LTCAmount { get; set; }
public string BTCAddress;
public string LTCAddress;
public double BTCAmount;
public double LTCAmount;
}
}