Add support for nested object property map.
This commit is contained in:
parent
8ed331ed95
commit
4693dcfa55
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MapTo.Sources;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
|
@ -63,5 +65,8 @@ namespace MapTo.Extensions
|
|||
p.NullableAnnotation == NullableAnnotation.Annotated &&
|
||||
targetProperty.NullableAnnotation == NullableAnnotation.Annotated));
|
||||
}
|
||||
|
||||
public static INamedTypeSymbol GetTypeByMetadataNameOrThrow(this Compilation compilation, string fullyQualifiedMetadataName) =>
|
||||
compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ?? throw new TypeLoadException($"Unable to find '{fullyQualifiedMetadataName}' type.");
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ namespace MapTo
|
|||
{
|
||||
foreach (var classSyntax in candidateClasses)
|
||||
{
|
||||
var mappingContext = MappingContext.Create(compilation, classSyntax, options);
|
||||
var mappingContext = new MappingContext(compilation, options, classSyntax);
|
||||
mappingContext.Diagnostics.ForEach(context.ReportDiagnostic);
|
||||
|
||||
if (mappingContext.Model is not null)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using MapTo.Extensions;
|
||||
|
@ -11,148 +10,178 @@ namespace MapTo
|
|||
{
|
||||
internal class MappingContext
|
||||
{
|
||||
private MappingContext(Compilation compilation)
|
||||
private readonly ClassDeclarationSyntax _classSyntax;
|
||||
private readonly Compilation _compilation;
|
||||
private readonly INamedTypeSymbol _ignorePropertyAttributeTypeSymbol;
|
||||
private readonly INamedTypeSymbol _mapFromAttributeTypeSymbol;
|
||||
private readonly INamedTypeSymbol _mapPropertyAttributeTypeSymbol;
|
||||
private readonly INamedTypeSymbol _mapTypeConverterAttributeTypeSymbol;
|
||||
private readonly SemanticModel _semanticModel;
|
||||
private readonly SourceGenerationOptions _sourceGenerationOptions;
|
||||
private readonly INamedTypeSymbol _typeConverterInterfaceTypeSymbol;
|
||||
|
||||
internal MappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, ClassDeclarationSyntax classSyntax)
|
||||
{
|
||||
Diagnostics = ImmutableArray<Diagnostic>.Empty;
|
||||
Compilation = compilation;
|
||||
_sourceGenerationOptions = sourceGenerationOptions;
|
||||
_classSyntax = classSyntax;
|
||||
_compilation = compilation;
|
||||
_semanticModel = _compilation.GetSemanticModel(_classSyntax.SyntaxTree);
|
||||
|
||||
IgnorePropertyAttributeTypeSymbol = compilation.GetTypeByMetadataName(IgnorePropertyAttributeSource.FullyQualifiedName)
|
||||
?? throw new TypeLoadException($"Unable to find '{IgnorePropertyAttributeSource.FullyQualifiedName}' type.");
|
||||
_ignorePropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(IgnorePropertyAttributeSource.FullyQualifiedName);
|
||||
_mapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapTypeConverterAttributeSource.FullyQualifiedName);
|
||||
_typeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(ITypeConverterSource.FullyQualifiedName);
|
||||
_mapPropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapPropertyAttributeSource.FullyQualifiedName);
|
||||
_mapFromAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapFromAttributeSource.FullyQualifiedName);
|
||||
|
||||
MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataName(MapTypeConverterAttributeSource.FullyQualifiedName)
|
||||
?? throw new TypeLoadException($"Unable to find '{MapTypeConverterAttributeSource.FullyQualifiedName}' type.");
|
||||
|
||||
TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataName(ITypeConverterSource.FullyQualifiedName)
|
||||
?? throw new TypeLoadException($"Unable to find '{ITypeConverterSource.FullyQualifiedName}' type.");
|
||||
|
||||
MapPropertyAttributeTypeSymbol = compilation.GetTypeByMetadataName(MapPropertyAttributeSource.FullyQualifiedName)
|
||||
?? throw new TypeLoadException($"Unable to find '{MapPropertyAttributeSource.FullyQualifiedName}' type.");
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private Compilation Compilation { get; }
|
||||
|
||||
public MappingModel? Model { get; private set; }
|
||||
|
||||
public ImmutableArray<Diagnostic> Diagnostics { get; private set; }
|
||||
|
||||
public INamedTypeSymbol IgnorePropertyAttributeTypeSymbol { get; }
|
||||
|
||||
public INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; }
|
||||
|
||||
public INamedTypeSymbol TypeConverterInterfaceTypeSymbol { get; }
|
||||
|
||||
public INamedTypeSymbol MapPropertyAttributeTypeSymbol { get; }
|
||||
|
||||
internal static MappingContext Create(Compilation compilation, ClassDeclarationSyntax classSyntax, SourceGenerationOptions sourceGenerationOptions)
|
||||
private void Initialize()
|
||||
{
|
||||
var context = new MappingContext(compilation);
|
||||
var root = classSyntax.GetCompilationUnit();
|
||||
|
||||
var semanticModel = compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
||||
if (!(semanticModel.GetDeclaredSymbol(classSyntax) is INamedTypeSymbol classTypeSymbol))
|
||||
if (!(_semanticModel.GetDeclaredSymbol(_classSyntax) is INamedTypeSymbol classTypeSymbol))
|
||||
{
|
||||
return context.ReportDiagnostic(DiagnosticProvider.TypeNotFoundError(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
||||
ReportDiagnostic(DiagnosticProvider.TypeNotFoundError(_classSyntax.GetLocation(), _classSyntax.Identifier.ValueText));
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceTypeSymbol = GetSourceTypeSymbol(semanticModel, classSyntax);
|
||||
var sourceTypeSymbol = GetSourceTypeSymbol(_classSyntax);
|
||||
if (sourceTypeSymbol is null)
|
||||
{
|
||||
return context.ReportDiagnostic(DiagnosticProvider.MapFromAttributeNotFoundError(classSyntax.GetLocation()));
|
||||
ReportDiagnostic(DiagnosticProvider.MapFromAttributeNotFoundError(_classSyntax.GetLocation()));
|
||||
return;
|
||||
}
|
||||
|
||||
var className = classSyntax.GetClassName();
|
||||
var className = _classSyntax.GetClassName();
|
||||
var sourceClassName = sourceTypeSymbol.Name;
|
||||
|
||||
var mappedProperties = GetMappedProperties(context, classTypeSymbol, sourceTypeSymbol);
|
||||
var mappedProperties = GetMappedProperties(classTypeSymbol, sourceTypeSymbol);
|
||||
if (!mappedProperties.Any())
|
||||
{
|
||||
return context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyFoundError(classSyntax.GetLocation(), classTypeSymbol, sourceTypeSymbol));
|
||||
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyFoundError(_classSyntax.GetLocation(), classTypeSymbol, sourceTypeSymbol));
|
||||
return;
|
||||
}
|
||||
|
||||
context.Model = new MappingModel(
|
||||
sourceGenerationOptions,
|
||||
classSyntax.GetNamespace(),
|
||||
classSyntax.Modifiers,
|
||||
Model = new MappingModel(
|
||||
_sourceGenerationOptions,
|
||||
_classSyntax.GetNamespace(),
|
||||
_classSyntax.Modifiers,
|
||||
className,
|
||||
sourceTypeSymbol.ContainingNamespace.ToString(),
|
||||
sourceClassName,
|
||||
sourceTypeSymbol.ToString(),
|
||||
mappedProperties.ToImmutableArray());
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private MappingContext ReportDiagnostic(Diagnostic diagnostic)
|
||||
{
|
||||
Diagnostics = Diagnostics.Add(diagnostic);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static INamedTypeSymbol? GetSourceTypeSymbol(SemanticModel semanticModel, ClassDeclarationSyntax classSyntax)
|
||||
{
|
||||
var sourceTypeExpressionSyntax = classSyntax
|
||||
.GetAttribute(MapFromAttributeSource.AttributeName)
|
||||
?.DescendantNodes()
|
||||
.OfType<TypeOfExpressionSyntax>()
|
||||
.SingleOrDefault();
|
||||
|
||||
return sourceTypeExpressionSyntax is not null ? semanticModel.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type as INamedTypeSymbol : null;
|
||||
}
|
||||
|
||||
private static ImmutableArray<MappedProperty> GetMappedProperties(MappingContext context, ITypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
||||
private ImmutableArray<MappedProperty> GetMappedProperties(ITypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
||||
{
|
||||
var mappedProperties = new List<MappedProperty>();
|
||||
var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType<IPropertySymbol>().ToArray();
|
||||
var classProperties = classSymbol.GetAllMembers().OfType<IPropertySymbol>().Where(p => !p.HasAttribute(context.IgnorePropertyAttributeTypeSymbol));
|
||||
var classProperties = classSymbol.GetAllMembers().OfType<IPropertySymbol>().Where(p => !p.HasAttribute(_ignorePropertyAttributeTypeSymbol));
|
||||
|
||||
foreach (var property in classProperties)
|
||||
{
|
||||
var sourceProperty = FindSourceProperty(context, sourceProperties, property);
|
||||
var sourceProperty = FindSourceProperty(sourceProperties, property);
|
||||
if (sourceProperty is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string? converterFullyQualifiedName = null;
|
||||
var converterParameters = new List<string>();
|
||||
var converterParameters = ImmutableArray<string>.Empty;
|
||||
string? mappedSourcePropertyType = null;
|
||||
|
||||
if (!context.Compilation.HasCompatibleTypes(sourceProperty, property))
|
||||
if (!_compilation.HasCompatibleTypes(sourceProperty, property))
|
||||
{
|
||||
var typeConverterAttribute = property.GetAttribute(context.MapTypeConverterAttributeTypeSymbol);
|
||||
if (typeConverterAttribute is null)
|
||||
if (!TryGetMapTypeConverter(property, sourceProperty, out converterFullyQualifiedName, out converterParameters) &&
|
||||
!TryGetNestedObjectMappings(property, out mappedSourcePropertyType))
|
||||
{
|
||||
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
||||
continue;
|
||||
}
|
||||
|
||||
var converterTypeSymbol = typeConverterAttribute.ConstructorArguments.First().Value as INamedTypeSymbol;
|
||||
if (converterTypeSymbol is null)
|
||||
{
|
||||
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
||||
continue;
|
||||
}
|
||||
|
||||
var baseInterface = GetTypeConverterBaseInterface(context, converterTypeSymbol, property, sourceProperty);
|
||||
if (baseInterface is null)
|
||||
{
|
||||
context.ReportDiagnostic(DiagnosticProvider.InvalidTypeConverterGenericTypesError(property, sourceProperty));
|
||||
continue;
|
||||
}
|
||||
|
||||
converterFullyQualifiedName = converterTypeSymbol.ToDisplayString();
|
||||
converterParameters.AddRange(GetTypeConverterParameters(typeConverterAttribute));
|
||||
}
|
||||
|
||||
mappedProperties.Add(new MappedProperty(property.Name, converterFullyQualifiedName, converterParameters.ToImmutableArray(), sourceProperty.Name));
|
||||
mappedProperties.Add(new MappedProperty(
|
||||
property.Name,
|
||||
property.Type.Name,
|
||||
converterFullyQualifiedName,
|
||||
converterParameters.ToImmutableArray(),
|
||||
sourceProperty.Name,
|
||||
mappedSourcePropertyType));
|
||||
}
|
||||
|
||||
return mappedProperties.ToImmutableArray();
|
||||
}
|
||||
|
||||
private static IPropertySymbol? FindSourceProperty(MappingContext context, IEnumerable<IPropertySymbol> sourceProperties, IPropertySymbol property)
|
||||
private bool TryGetNestedObjectMappings(IPropertySymbol property, out string? mappedSourcePropertyType)
|
||||
{
|
||||
mappedSourcePropertyType = null;
|
||||
|
||||
if (!Diagnostics.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var nestedSourceMapFromAttribute = property.Type.GetAttribute(_mapFromAttributeTypeSymbol);
|
||||
if (nestedSourceMapFromAttribute is null)
|
||||
{
|
||||
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
||||
return false;
|
||||
}
|
||||
|
||||
var nestedAttributeSyntax = nestedSourceMapFromAttribute.ApplicationSyntaxReference?.GetSyntax() as AttributeSyntax;
|
||||
if (nestedAttributeSyntax is null)
|
||||
{
|
||||
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
||||
return false;
|
||||
}
|
||||
|
||||
var nestedSourceTypeSymbol = GetSourceTypeSymbol(nestedAttributeSyntax);
|
||||
if (nestedSourceTypeSymbol is null)
|
||||
{
|
||||
ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
||||
return false;
|
||||
}
|
||||
|
||||
mappedSourcePropertyType = nestedSourceTypeSymbol.Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetMapTypeConverter(IPropertySymbol property, IPropertySymbol sourceProperty, out string? converterFullyQualifiedName, out ImmutableArray<string> converterParameters)
|
||||
{
|
||||
converterFullyQualifiedName = null;
|
||||
converterParameters = ImmutableArray<string>.Empty;
|
||||
|
||||
if (!Diagnostics.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var typeConverterAttribute = property.GetAttribute(_mapTypeConverterAttributeTypeSymbol);
|
||||
if (!(typeConverterAttribute?.ConstructorArguments.First().Value is INamedTypeSymbol converterTypeSymbol))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var baseInterface = GetTypeConverterBaseInterface(converterTypeSymbol, property, sourceProperty);
|
||||
if (baseInterface is null)
|
||||
{
|
||||
ReportDiagnostic(DiagnosticProvider.InvalidTypeConverterGenericTypesError(property, sourceProperty));
|
||||
return false;
|
||||
}
|
||||
|
||||
converterFullyQualifiedName = converterTypeSymbol.ToDisplayString();
|
||||
converterParameters = GetTypeConverterParameters(typeConverterAttribute);
|
||||
return true;
|
||||
}
|
||||
|
||||
private IPropertySymbol? FindSourceProperty(IEnumerable<IPropertySymbol> sourceProperties, IPropertySymbol property)
|
||||
{
|
||||
var propertyName = property
|
||||
.GetAttribute(context.MapPropertyAttributeTypeSymbol)
|
||||
.GetAttribute(_mapPropertyAttributeTypeSymbol)
|
||||
?.NamedArguments
|
||||
.SingleOrDefault(a => a.Key == MapPropertyAttributeSource.SourcePropertyNamePropertyName)
|
||||
.Value.Value as string ?? property.Name;
|
||||
|
@ -160,22 +189,40 @@ namespace MapTo
|
|||
return sourceProperties.SingleOrDefault(p => p.Name == propertyName);
|
||||
}
|
||||
|
||||
private static INamedTypeSymbol? GetTypeConverterBaseInterface(MappingContext context, ITypeSymbol converterTypeSymbol, IPropertySymbol property, IPropertySymbol sourceProperty)
|
||||
private INamedTypeSymbol? GetTypeConverterBaseInterface(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(i.ConstructedFrom, _typeConverterInterfaceTypeSymbol) &&
|
||||
SymbolEqualityComparer.Default.Equals(sourceProperty.Type, i.TypeArguments[0]) &&
|
||||
SymbolEqualityComparer.Default.Equals(property.Type, i.TypeArguments[1]));
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetTypeConverterParameters(AttributeData typeConverterAttribute)
|
||||
private static ImmutableArray<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());
|
||||
? ImmutableArray<string>.Empty
|
||||
: converterParameter.Values.Where(v => v.Value is not null).Select(v => v.Value!.ToSourceCodeString()).ToImmutableArray();
|
||||
}
|
||||
|
||||
private void ReportDiagnostic(Diagnostic diagnostic)
|
||||
{
|
||||
Diagnostics = Diagnostics.Add(diagnostic);
|
||||
}
|
||||
|
||||
private INamedTypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classDeclarationSyntax) =>
|
||||
GetSourceTypeSymbol(classDeclarationSyntax.GetAttribute(MapFromAttributeSource.AttributeName));
|
||||
|
||||
private INamedTypeSymbol? GetSourceTypeSymbol(AttributeSyntax? attributeSyntax)
|
||||
{
|
||||
var sourceTypeExpressionSyntax = attributeSyntax
|
||||
?.DescendantNodes()
|
||||
.OfType<TypeOfExpressionSyntax>()
|
||||
.SingleOrDefault();
|
||||
|
||||
return sourceTypeExpressionSyntax is not null ? _semanticModel.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type as INamedTypeSymbol : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,9 +9,11 @@ namespace MapTo
|
|||
|
||||
internal record MappedProperty(
|
||||
string Name,
|
||||
string Type,
|
||||
string? TypeConverter,
|
||||
ImmutableArray<string> TypeConverterParameters,
|
||||
string SourcePropertyName);
|
||||
string SourcePropertyName,
|
||||
string? MappedSourcePropertyTypeName);
|
||||
|
||||
internal record MappingModel (
|
||||
SourceGenerationOptions Options,
|
||||
|
|
|
@ -70,7 +70,9 @@ namespace MapTo.Sources
|
|||
{
|
||||
if (property.TypeConverter is null)
|
||||
{
|
||||
builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};");
|
||||
builder.WriteLine(property.MappedSourcePropertyTypeName is null
|
||||
? $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"
|
||||
: $"{property.Name} = new {property.Type}({sourceClassParameterName}.{property.SourcePropertyName});");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -333,5 +333,101 @@ namespace Test
|
|||
diagnostics.ShouldBeSuccessful();
|
||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_HasNestedObjectPropertyTypeHasMapFromAttribute_Should_UseContinueToMap()
|
||||
{
|
||||
// Arrange
|
||||
var source = GetSourceText(new SourceGeneratorOptions(
|
||||
SourceClassNamespace: "Test",
|
||||
PropertyBuilder: b => b.WriteLine("public B InnerProp1 { get; }"),
|
||||
SourcePropertyBuilder: b => b.WriteLine("public A InnerProp1 { get; }")));
|
||||
|
||||
source += @"
|
||||
namespace Test
|
||||
{
|
||||
public class A { public int Prop1 { get; } }
|
||||
|
||||
[MapTo.MapFrom(typeof(A))]
|
||||
public partial class B { public int Prop1 { get; }}
|
||||
}
|
||||
".Trim();
|
||||
|
||||
var expectedResult = @"
|
||||
partial class Foo
|
||||
{
|
||||
public Foo(Test.Baz baz)
|
||||
{
|
||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||
|
||||
Prop1 = baz.Prop1;
|
||||
Prop2 = baz.Prop2;
|
||||
Prop3 = baz.Prop3;
|
||||
InnerProp1 = new B(baz.InnerProp1);
|
||||
}
|
||||
".Trim();
|
||||
|
||||
// Act
|
||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||
|
||||
// Assert
|
||||
diagnostics.ShouldBeSuccessful();
|
||||
compilation.SyntaxTrees.ToArray()[^2].ToString().ShouldContain(expectedResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_HasNestedObjectPropertyTypeDoesNotHaveMapFromAttribute_Should_ReportError()
|
||||
{
|
||||
// Arrange
|
||||
var source = GetSourceText(new SourceGeneratorOptions(
|
||||
SourceClassNamespace: "Test",
|
||||
PropertyBuilder: b => b.WriteLine("public FooInner1 InnerProp1 { get; }"),
|
||||
SourcePropertyBuilder: b => b.WriteLine("public BazInner1 InnerProp1 { get; }")));
|
||||
|
||||
source += @"
|
||||
namespace Test
|
||||
{
|
||||
public class FooInner1 { public int Prop1 { get; } }
|
||||
|
||||
public partial class BazInner1 { public int Prop1 { get; }}
|
||||
}
|
||||
".Trim();
|
||||
|
||||
// Act
|
||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||
|
||||
// Assert
|
||||
var expectedError = DiagnosticProvider.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("InnerProp1", compilation));
|
||||
diagnostics.ShouldBeUnsuccessful(expectedError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_HasNestedObjectPropertyTypeHasMapFromAttributeToDifferentType_Should_ReportError()
|
||||
{
|
||||
// Arrange
|
||||
var source = GetSourceText(new SourceGeneratorOptions(
|
||||
SourceClassNamespace: "Test",
|
||||
PropertyBuilder: b => b.WriteLine("public FooInner1 InnerProp1 { get; }"),
|
||||
SourcePropertyBuilder: b => b.WriteLine("public BazInner1 InnerProp1 { get; }")));
|
||||
|
||||
source += @"
|
||||
namespace Test
|
||||
{
|
||||
public class FooInner1 { public int Prop1 { get; } }
|
||||
|
||||
public class FooInner2 { public int Prop1 { get; } }
|
||||
|
||||
[MapTo.MapFrom(typeof(FooInner2))]
|
||||
public partial class BazInner1 { public int Prop1 { get; }}
|
||||
}
|
||||
".Trim();
|
||||
|
||||
// Act
|
||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||
|
||||
// Assert
|
||||
var expectedError = DiagnosticProvider.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("InnerProp1", compilation));
|
||||
diagnostics.ShouldBeUnsuccessful(expectedError);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue