This commit is contained in:
Mohammadreza Taikandi 2021-01-25 09:51:57 +00:00
parent 13c5279e15
commit 2e28ad8143
2 changed files with 48 additions and 34 deletions

View File

@ -36,22 +36,32 @@ namespace MapTo.Extensions
((a.Name as QualifiedNameSyntax)?.Right as IdentifierNameSyntax)?.Identifier.ValueText == attributeName); ((a.Name as QualifiedNameSyntax)?.Right as IdentifierNameSyntax)?.Identifier.ValueText == attributeName);
} }
public static bool HasAttribute(this ISymbol symbol, ITypeSymbol attributeSymbol) => public static bool HasAttribute(this ISymbol symbol, ITypeSymbol attributeSymbol) =>
symbol.GetAttributes().Any(a => a.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) == true); symbol.GetAttributes().Any(a => a.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) == true);
public static IEnumerable<AttributeData> GetAttributes(this ISymbol symbol, ITypeSymbol attributeSymbol) => public static IEnumerable<AttributeData> GetAttributes(this ISymbol symbol, ITypeSymbol attributeSymbol) =>
symbol.GetAttributes().Where(a => a.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) == true); symbol.GetAttributes().Where(a => a.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) == true);
public static AttributeData? GetAttribute(this ISymbol symbol, ITypeSymbol attributeSymbol) => public static AttributeData? GetAttribute(this ISymbol symbol, ITypeSymbol attributeSymbol) =>
symbol.GetAttributes(attributeSymbol).FirstOrDefault(); symbol.GetAttributes(attributeSymbol).FirstOrDefault();
public static string? GetNamespace(this ClassDeclarationSyntax classDeclarationSyntax) public static string? GetNamespace(this ClassDeclarationSyntax classDeclarationSyntax) =>
{ classDeclarationSyntax.Ancestors()
return classDeclarationSyntax.Ancestors()
.OfType<NamespaceDeclarationSyntax>() .OfType<NamespaceDeclarationSyntax>()
.FirstOrDefault() .FirstOrDefault()
?.Name ?.Name
.ToString(); .ToString();
public static bool HasCompatibleTypes(this Compilation compilation, IPropertySymbol sourceProperty, IPropertySymbol destinationProperty) =>
SymbolEqualityComparer.Default.Equals(destinationProperty.Type, sourceProperty.Type) || compilation.HasImplicitConversion(sourceProperty.Type, destinationProperty.Type);
public static IPropertySymbol? FindProperty(this IEnumerable<IPropertySymbol> properties, IPropertySymbol targetProperty)
{
return properties.SingleOrDefault(p =>
p.Name == targetProperty.Name &&
(p.NullableAnnotation != NullableAnnotation.Annotated ||
p.NullableAnnotation == NullableAnnotation.Annotated &&
targetProperty.NullableAnnotation == NullableAnnotation.Annotated));
} }
} }
} }

View File

@ -11,8 +11,6 @@ namespace MapTo
{ {
internal class MappingContext internal class MappingContext
{ {
private Compilation Compilation { get; }
private MappingContext(Compilation compilation) private MappingContext(Compilation compilation)
{ {
Diagnostics = ImmutableArray<Diagnostic>.Empty; Diagnostics = ImmutableArray<Diagnostic>.Empty;
@ -28,6 +26,8 @@ namespace MapTo
?? throw new TypeLoadException($"Unable to find '{ITypeConverterSource.FullyQualifiedName}' type."); ?? throw new TypeLoadException($"Unable to find '{ITypeConverterSource.FullyQualifiedName}' type.");
} }
private Compilation Compilation { get; }
public INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; } public INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; }
public INamedTypeSymbol TypeConverterInterfaceTypeSymbol { get; } public INamedTypeSymbol TypeConverterInterfaceTypeSymbol { get; }
@ -38,12 +38,6 @@ namespace MapTo
public INamedTypeSymbol IgnorePropertyAttributeTypeSymbol { get; } public INamedTypeSymbol IgnorePropertyAttributeTypeSymbol { get; }
private MappingContext ReportDiagnostic(Diagnostic diagnostic)
{
Diagnostics = Diagnostics.Add(diagnostic);
return this;
}
internal static MappingContext Create(Compilation compilation, ClassDeclarationSyntax classSyntax, SourceGenerationOptions sourceGenerationOptions) internal static MappingContext Create(Compilation compilation, ClassDeclarationSyntax classSyntax, SourceGenerationOptions sourceGenerationOptions)
{ {
var context = new MappingContext(compilation); var context = new MappingContext(compilation);
@ -83,6 +77,12 @@ namespace MapTo
return context; return context;
} }
private MappingContext ReportDiagnostic(Diagnostic diagnostic)
{
Diagnostics = Diagnostics.Add(diagnostic);
return this;
}
private static INamedTypeSymbol? GetSourceTypeSymbol(SemanticModel semanticModel, ClassDeclarationSyntax classSyntax) private static INamedTypeSymbol? GetSourceTypeSymbol(SemanticModel semanticModel, ClassDeclarationSyntax classSyntax)
{ {
var sourceTypeExpressionSyntax = classSyntax var sourceTypeExpressionSyntax = classSyntax
@ -102,12 +102,7 @@ namespace MapTo
foreach (var property in classProperties) foreach (var property in classProperties)
{ {
var sourceProperty = sourceProperties.SingleOrDefault(p => var sourceProperty = sourceProperties.FindProperty(property);
p.Name == property.Name &&
(p.NullableAnnotation != NullableAnnotation.Annotated ||
p.NullableAnnotation == NullableAnnotation.Annotated &&
property.NullableAnnotation == NullableAnnotation.Annotated));
if (sourceProperty is null) if (sourceProperty is null)
{ {
continue; continue;
@ -115,7 +110,8 @@ namespace MapTo
string? converterFullyQualifiedName = null; string? converterFullyQualifiedName = null;
var converterParameters = new List<string>(); var converterParameters = new List<string>();
if (!SymbolEqualityComparer.Default.Equals(property.Type, sourceProperty.Type) && !context.Compilation.HasImplicitConversion(sourceProperty.Type, property.Type))
if (!context.Compilation.HasCompatibleTypes(sourceProperty, property))
{ {
var typeConverterAttribute = property.GetAttribute(context.MapTypeConverterAttributeTypeSymbol); var typeConverterAttribute = property.GetAttribute(context.MapTypeConverterAttributeTypeSymbol);
if (typeConverterAttribute is null) if (typeConverterAttribute is null)
@ -123,7 +119,7 @@ namespace MapTo
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property)); context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
continue; continue;
} }
var converterTypeSymbol = typeConverterAttribute.ConstructorArguments.First().Value as INamedTypeSymbol; var converterTypeSymbol = typeConverterAttribute.ConstructorArguments.First().Value as INamedTypeSymbol;
if (converterTypeSymbol is null) if (converterTypeSymbol is null)
{ {
@ -131,12 +127,7 @@ namespace MapTo
continue; continue;
} }
var baseInterface = converterTypeSymbol.AllInterfaces var baseInterface = GetTypeConverterBaseInterface(context, converterTypeSymbol, property, sourceProperty);
.SingleOrDefault(i => SymbolEqualityComparer.Default.Equals(i.ConstructedFrom, context.TypeConverterInterfaceTypeSymbol) &&
i.TypeArguments.Length == 2 &&
SymbolEqualityComparer.Default.Equals(sourceProperty.Type, i.TypeArguments[0]) &&
SymbolEqualityComparer.Default.Equals(property.Type, i.TypeArguments[1]));
if (baseInterface is null) if (baseInterface is null)
{ {
context.ReportDiagnostic(DiagnosticProvider.InvalidTypeConverterGenericTypesError(property, sourceProperty)); context.ReportDiagnostic(DiagnosticProvider.InvalidTypeConverterGenericTypesError(property, sourceProperty));
@ -144,12 +135,7 @@ namespace MapTo
} }
converterFullyQualifiedName = converterTypeSymbol.ToDisplayString(); converterFullyQualifiedName = converterTypeSymbol.ToDisplayString();
converterParameters.AddRange(GetTypeConverterParameters(typeConverterAttribute));
var converterParameter = typeConverterAttribute.ConstructorArguments.Skip(1).FirstOrDefault();
if (!converterParameter.IsNull )
{
converterParameters.AddRange(converterParameter.Values.Where(v => v.Value is not null).Select(v => v.Value!.ToSourceCodeString()));
}
} }
mappedProperties.Add(new MappedProperty(property.Name, converterFullyQualifiedName, converterParameters.ToImmutableArray())); mappedProperties.Add(new MappedProperty(property.Name, converterFullyQualifiedName, converterParameters.ToImmutableArray()));
@ -157,5 +143,23 @@ namespace MapTo
return mappedProperties.ToImmutableArray(); return mappedProperties.ToImmutableArray();
} }
private static INamedTypeSymbol? GetTypeConverterBaseInterface(MappingContext context, ITypeSymbol converterTypeSymbol, IPropertySymbol property, IPropertySymbol sourceProperty)
{
return converterTypeSymbol.AllInterfaces
.SingleOrDefault(i =>
i.TypeArguments.Length == 2 &&
SymbolEqualityComparer.Default.Equals(i.ConstructedFrom, context.TypeConverterInterfaceTypeSymbol) &&
SymbolEqualityComparer.Default.Equals(sourceProperty.Type, i.TypeArguments[0]) &&
SymbolEqualityComparer.Default.Equals(property.Type, i.TypeArguments[1]));
}
private static IEnumerable<string> GetTypeConverterParameters(AttributeData typeConverterAttribute)
{
var converterParameter = typeConverterAttribute.ConstructorArguments.Skip(1).FirstOrDefault();
return converterParameter.IsNull
? Enumerable.Empty<string>()
: converterParameter.Values.Where(v => v.Value is not null).Select(v => v.Value!.ToSourceCodeString());
}
} }
} }