Add parameter to ITypeConverter.

This commit is contained in:
Mohammadreza Taikandi 2021-01-23 11:05:58 +00:00
parent 2ef7ded450
commit 13c5279e15
10 changed files with 128 additions and 44 deletions

View File

@ -26,7 +26,7 @@ namespace MapTo
Create($"{ErrorId}031", property.Locations.FirstOrDefault(), "Type Mismatch", $"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}'."); Create($"{ErrorId}031", property.Locations.FirstOrDefault(), "Type Mismatch", $"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(IPropertySymbol property, IPropertySymbol sourceProperty) => internal static Diagnostic InvalidTypeConverterGenericTypesError(IPropertySymbol property, IPropertySymbol sourceProperty) =>
Create($"{ErrorId}032", property.Locations.FirstOrDefault(), "Type Mismatch", $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{TypeConverterSource.InterfaceName}<{sourceProperty.Type.ToDisplayString()}, {property.Type.ToDisplayString()}>'."); Create($"{ErrorId}032", property.Locations.FirstOrDefault(), "Type Mismatch", $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{ITypeConverterSource.InterfaceName}<{sourceProperty.Type.ToDisplayString()}, {property.Type.ToDisplayString()}>'.");
internal static Diagnostic ConfigurationParseError(string error) => internal static Diagnostic ConfigurationParseError(string error) =>
Create($"{ErrorId}040", Location.None, "Incorrect Configuration", error); Create($"{ErrorId}040", Location.None, "Incorrect Configuration", error);

View File

@ -1,7 +1,17 @@
namespace MapTo.Extensions using System.Threading.Tasks;
namespace MapTo.Extensions
{ {
internal static class StringExtensions internal static class StringExtensions
{ {
public static string ToCamelCase(this string value) => string.IsNullOrWhiteSpace(value) ? value : $"{char.ToLower(value[0])}{value.Substring(1)}"; public static string ToCamelCase(this string value) => string.IsNullOrWhiteSpace(value) ? value : $"{char.ToLower(value[0])}{value.Substring(1)}";
public static string ToSourceCodeString(this object? value) => value switch
{
null => "null",
string strValue => $"\"{strValue}\"",
char charValue => $"'{charValue}'",
_ => value.ToString()
};
} }
} }

View File

@ -24,7 +24,7 @@ namespace MapTo
var compilation = context.Compilation var compilation = context.Compilation
.AddSource(ref context, MapFromAttributeSource.Generate(options)) .AddSource(ref context, MapFromAttributeSource.Generate(options))
.AddSource(ref context, IgnorePropertyAttributeSource.Generate(options)) .AddSource(ref context, IgnorePropertyAttributeSource.Generate(options))
.AddSource(ref context, TypeConverterSource.Generate(options)) .AddSource(ref context, ITypeConverterSource.Generate(options))
.AddSource(ref context, MapTypeConverterAttributeSource.Generate(options)); .AddSource(ref context, MapTypeConverterAttributeSource.Generate(options));
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any()) if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())

View File

@ -24,8 +24,8 @@ namespace MapTo
MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataName(MapTypeConverterAttributeSource.FullyQualifiedName) MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataName(MapTypeConverterAttributeSource.FullyQualifiedName)
?? throw new TypeLoadException($"Unable to find '{MapTypeConverterAttributeSource.FullyQualifiedName}' type."); ?? throw new TypeLoadException($"Unable to find '{MapTypeConverterAttributeSource.FullyQualifiedName}' type.");
TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataName(TypeConverterSource.FullyQualifiedName) TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataName(ITypeConverterSource.FullyQualifiedName)
?? throw new TypeLoadException($"Unable to find '{TypeConverterSource.FullyQualifiedName}' type."); ?? throw new TypeLoadException($"Unable to find '{ITypeConverterSource.FullyQualifiedName}' type.");
} }
public INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; } public INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; }
@ -114,9 +114,17 @@ namespace MapTo
} }
string? converterFullyQualifiedName = null; string? converterFullyQualifiedName = null;
var converterParameters = new List<string>();
if (!SymbolEqualityComparer.Default.Equals(property.Type, sourceProperty.Type) && !context.Compilation.HasImplicitConversion(sourceProperty.Type, property.Type)) if (!SymbolEqualityComparer.Default.Equals(property.Type, sourceProperty.Type) && !context.Compilation.HasImplicitConversion(sourceProperty.Type, property.Type))
{ {
var converterTypeSymbol = property.GetAttribute(context.MapTypeConverterAttributeTypeSymbol)?.ConstructorArguments.First().Value as INamedTypeSymbol; var typeConverterAttribute = property.GetAttribute(context.MapTypeConverterAttributeTypeSymbol);
if (typeConverterAttribute is null)
{
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
continue;
}
var converterTypeSymbol = typeConverterAttribute.ConstructorArguments.First().Value as INamedTypeSymbol;
if (converterTypeSymbol is null) if (converterTypeSymbol is null)
{ {
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property)); context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
@ -136,9 +144,15 @@ namespace MapTo
} }
converterFullyQualifiedName = converterTypeSymbol.ToDisplayString(); converterFullyQualifiedName = converterTypeSymbol.ToDisplayString();
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)); mappedProperties.Add(new MappedProperty(property.Name, converterFullyQualifiedName, converterParameters.ToImmutableArray()));
} }
return mappedProperties.ToImmutableArray(); return mappedProperties.ToImmutableArray();

View File

@ -16,7 +16,7 @@ namespace MapTo
); );
} }
internal record MappedProperty(string Name, string? TypeConverter); internal record MappedProperty(string Name, string? TypeConverter, ImmutableArray<string> TypeConverterParameters);
internal record MappingModel ( internal record MappingModel (
SourceGenerationOptions Options, SourceGenerationOptions Options,

View File

@ -1,9 +1,11 @@
using Microsoft.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using static MapTo.Sources.Constants; using static MapTo.Sources.Constants;
namespace MapTo.Sources namespace MapTo.Sources
{ {
internal class TypeConverterSource [SuppressMessage("ReSharper", "InconsistentNaming")]
internal static class ITypeConverterSource
{ {
internal const string InterfaceName = "ITypeConverter"; internal const string InterfaceName = "ITypeConverter";
internal const string FullyQualifiedName = RootNamespace + "." + InterfaceName + "`2"; internal const string FullyQualifiedName = RootNamespace + "." + InterfaceName + "`2";
@ -34,14 +36,15 @@ namespace MapTo.Sources
{ {
builder builder
.WriteLine("/// <summary>") .WriteLine("/// <summary>")
.WriteLine("/// Converts the value of <paramref name=\"value\"/> object to <typeparamref name=\"TDestination\"/>.") .WriteLine("/// Converts the value of <paramref name=\"source\"/> object to <typeparamref name=\"TDestination\"/>.")
.WriteLine("/// </summary>") .WriteLine("/// </summary>")
.WriteLine("/// <param name=\"value\">The object to convert.</param>") .WriteLine("/// <param name=\"source\">The <see cref=\"TSource\"/> to convert.</param>")
.WriteLine($"/// <param name=\"converterParameters\">The parameter list passed to the <see cref=\"{MapTypeConverterAttributeSource.AttributeClassName}\"/></param>")
.WriteLine("/// <returns><typeparamref name=\"TDestination\"/> object.</returns>"); .WriteLine("/// <returns><typeparamref name=\"TDestination\"/> object.</returns>");
} }
builder builder
.WriteLine("TDestination Convert(TSource source);") .WriteLine("TDestination Convert(TSource source, object[] converterParameters);")
.WriteClosingBracket() .WriteClosingBracket()
.WriteClosingBracket(); .WriteClosingBracket();

View File

@ -66,13 +66,17 @@ namespace MapTo.Sources
foreach (var property in model.MappedProperties) foreach (var property in model.MappedProperties)
{ {
if (property.TypeConverter is not null) if (property.TypeConverter is null)
{ {
builder.WriteLine($"{property.Name} = new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.Name});"); builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.Name};");
} }
else else
{ {
builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.Name};"); var parameters = property.TypeConverterParameters.IsEmpty
? "null"
: $"new object[] {{ {string.Join(", ", property.TypeConverterParameters)} }}";
builder.WriteLine($"{property.Name} = new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.Name}, {parameters});");
} }
} }

View File

@ -9,6 +9,7 @@ namespace MapTo.Sources
internal const string AttributeClassName = AttributeName + "Attribute"; internal const string AttributeClassName = AttributeName + "Attribute";
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
internal const string ConverterPropertyName = "Converter"; internal const string ConverterPropertyName = "Converter";
internal const string ConverterParametersPropertyName = "ConverterParameters";
internal static SourceCode Generate(SourceGenerationOptions options) internal static SourceCode Generate(SourceGenerationOptions options)
{ {
@ -37,13 +38,16 @@ namespace MapTo.Sources
builder builder
.WriteLine("/// <summary>") .WriteLine("/// <summary>")
.WriteLine($"/// Initializes a new instance of <see cref=\"{AttributeClassName}\"/>.") .WriteLine($"/// Initializes a new instance of <see cref=\"{AttributeClassName}\"/>.")
.WriteLine("/// </summary>"); .WriteLine("/// </summary>")
.WriteLine($"/// <param name=\"converter\">The <see cref=\"{ITypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.</param>")
.WriteLine("/// <param name=\"converterParameters\">The parameter list to pass to the <paramref name=\"converter\"/> during the type conversion.</param>");
} }
builder builder
.WriteLine($"public {AttributeClassName}(Type converter)") .WriteLine($"public {AttributeClassName}(Type converter, object[] converterParameters = null)")
.WriteOpeningBracket() .WriteOpeningBracket()
.WriteLine($"{ConverterPropertyName} = converter;") .WriteLine($"{ConverterPropertyName} = converter;")
.WriteLine($"{ConverterParametersPropertyName} = converterParameters;")
.WriteClosingBracket() .WriteClosingBracket()
.WriteLine(); .WriteLine();
@ -51,12 +55,24 @@ namespace MapTo.Sources
{ {
builder builder
.WriteLine("/// <summary>") .WriteLine("/// <summary>")
.WriteLine($"/// Gets or sets the <see cref=\"{TypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.") .WriteLine($"/// Gets or sets the <see cref=\"{ITypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.")
.WriteLine("/// </summary>"); .WriteLine("/// </summary>");
} }
builder builder
.WriteLine($"public Type {ConverterPropertyName} {{ get; }}") .WriteLine($"public Type {ConverterPropertyName} {{ get; }}")
.WriteLine();
if (options.GenerateXmlDocument)
{
builder
.WriteLine("/// <summary>")
.WriteLine($"/// Gets the parameter list to pass to the <see cref=\"{ConverterPropertyName}\"/> during the type conversion.")
.WriteLine("/// </summary>");
}
builder
.WriteLine($"public object[] {ConverterParametersPropertyName} {{ get; }}")
.WriteClosingBracket() .WriteClosingBracket()
.WriteClosingBracket(); .WriteClosingBracket();

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MapTo.Extensions; using MapTo.Extensions;
@ -10,7 +9,6 @@ using MapTo.Tests.Infrastructure;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Shouldly; using Shouldly;
using Xunit; using Xunit;
using static MapTo.Extensions.GeneratorExecutionContextExtensions; using static MapTo.Extensions.GeneratorExecutionContextExtensions;
@ -65,7 +63,7 @@ namespace MapTo
builder.AppendLine("// Test source code."); builder.AppendLine("// Test source code.");
builder.AppendLine("//"); builder.AppendLine("//");
builder.AppendLine(); builder.AppendLine();
if (options.UseMapToNamespace) if (options.UseMapToNamespace)
{ {
builder.AppendFormat("using {0};", Constants.RootNamespace).AppendLine(); builder.AppendFormat("using {0};", Constants.RootNamespace).AppendLine();
@ -91,7 +89,7 @@ namespace MapTo
builder builder
.PadLeft(Indent1) .PadLeft(Indent1)
.AppendLine(options.UseMapToNamespace ? "[MapFrom(typeof(Baz))]": "[MapTo.MapFrom(typeof(Baz))]") .AppendLine(options.UseMapToNamespace ? "[MapFrom(typeof(Baz))]" : "[MapTo.MapFrom(typeof(Baz))]")
.PadLeft(Indent1).Append("public partial class Foo") .PadLeft(Indent1).Append("public partial class Foo")
.AppendOpeningBracket(Indent1); .AppendOpeningBracket(Indent1);
@ -182,10 +180,10 @@ namespace MapTo
.PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }"); .PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }");
}, },
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }"))); SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }")));
// Act // Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
// Assert // Assert
var expectedError = DiagnosticProvider.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("Prop4", compilation)); var expectedError = DiagnosticProvider.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("Prop4", compilation));
@ -221,7 +219,7 @@ namespace MapTo
// Act // Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
// Assert // Assert
diagnostics.ShouldBeSuccessful(); diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult); compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
@ -332,7 +330,7 @@ namespace Test
// Assert // Assert
var fooType = compilation.GetTypeByMetadataName("Test.Foo"); var fooType = compilation.GetTypeByMetadataName("Test.Foo");
fooType.ShouldNotBeNull(); fooType.ShouldNotBeNull();
var bazType = compilation.GetTypeByMetadataName("Test.Baz"); var bazType = compilation.GetTypeByMetadataName("Test.Baz");
bazType.ShouldNotBeNull(); bazType.ShouldNotBeNull();
@ -384,7 +382,7 @@ namespace Test
{ {
// Arrange // Arrange
const string source = ""; const string source = "";
var expectedTypes = new[] { IgnorePropertyAttributeSource.AttributeName, MapFromAttributeSource.AttributeName, TypeConverterSource.InterfaceName }; var expectedTypes = new[] { IgnorePropertyAttributeSource.AttributeName, MapFromAttributeSource.AttributeName, ITypeConverterSource.InterfaceName };
// Act // Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source); var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
@ -490,7 +488,7 @@ namespace Test
diagnostics.ShouldBeSuccessful(); diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim()); compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
} }
[Fact] [Fact]
public void VerifyTypeConverterInterface() public void VerifyTypeConverterInterface()
{ {
@ -503,7 +501,7 @@ namespace MapTo
{{ {{
public interface ITypeConverter<in TSource, out TDestination> public interface ITypeConverter<in TSource, out TDestination>
{{ {{
TDestination Convert(TSource source); TDestination Convert(TSource source, object[] converterParameters);
}} }}
}} }}
".Trim(); ".Trim();
@ -513,9 +511,9 @@ namespace MapTo
// Assert // Assert
diagnostics.ShouldBeSuccessful(); diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.ShouldContainSource(TypeConverterSource.InterfaceName, expectedInterface); compilation.SyntaxTrees.ShouldContainSource(ITypeConverterSource.InterfaceName, expectedInterface);
} }
[Fact] [Fact]
public void VerifyMapTypeConverterAttribute() public void VerifyMapTypeConverterAttribute()
{ {
@ -530,12 +528,15 @@ namespace MapTo
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class MapTypeConverterAttribute : Attribute public sealed class MapTypeConverterAttribute : Attribute
{{ {{
public MapTypeConverterAttribute(Type converter) public MapTypeConverterAttribute(Type converter, object[] converterParameters = null)
{{ {{
Converter = converter; Converter = converter;
ConverterParameters = converterParameters;
}} }}
public Type Converter {{ get; }} public Type Converter {{ get; }}
public object[] ConverterParameters {{ get; }}
}} }}
}} }}
".Trim(); ".Trim();
@ -547,7 +548,7 @@ namespace MapTo
diagnostics.ShouldBeSuccessful(); diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.ShouldContainSource(MapTypeConverterAttributeSource.AttributeName, expectedInterface); compilation.SyntaxTrees.ShouldContainSource(MapTypeConverterAttributeSource.AttributeName, expectedInterface);
} }
[Fact] [Fact]
public void When_FoundMatchingPropertyNameWithDifferentImplicitlyConvertibleType_Should_GenerateTheProperty() public void When_FoundMatchingPropertyNameWithDifferentImplicitlyConvertibleType_Should_GenerateTheProperty()
{ {
@ -582,7 +583,7 @@ namespace MapTo
diagnostics.ShouldBeSuccessful(); diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult); compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
} }
[Fact] [Fact]
public void When_FoundMatchingPropertyNameWithIncorrectConverterType_ShouldReportError() public void When_FoundMatchingPropertyNameWithIncorrectConverterType_ShouldReportError()
{ {
@ -606,19 +607,19 @@ namespace Test
public class Prop4Converter: ITypeConverter<string, int> public class Prop4Converter: ITypeConverter<string, int>
{ {
public int Convert(string source) => int.Parse(source); public int Convert(string source, object[] converterParameters) => int.Parse(source);
} }
} }
"; ";
// Act // Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
// Assert // Assert
var expectedError = DiagnosticProvider.InvalidTypeConverterGenericTypesError(GetSourcePropertySymbol("Prop4", compilation), GetSourcePropertySymbol("Prop4", compilation, "Baz")); var expectedError = DiagnosticProvider.InvalidTypeConverterGenericTypesError(GetSourcePropertySymbol("Prop4", compilation), GetSourcePropertySymbol("Prop4", compilation, "Baz"));
diagnostics.ShouldBeUnsuccessful(expectedError); diagnostics.ShouldBeUnsuccessful(expectedError);
} }
[Fact] [Fact]
public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterToAssignProperties() public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterToAssignProperties()
{ {
@ -640,16 +641,52 @@ namespace Test
public class Prop4Converter: ITypeConverter<string, long> public class Prop4Converter: ITypeConverter<string, long>
{ {
public long Convert(string source) => long.Parse(source); public long Convert(string source, object[] converterParameters) => long.Parse(source);
} }
} }
"; ";
const string expectedSyntax = "Prop4 = new Test.Prop4Converter().Convert(baz.Prop4);"; const string expectedSyntax = "Prop4 = new Test.Prop4Converter().Convert(baz.Prop4, null);";
// Act // Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
// Assert
diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedSyntax);
}
[Fact]
public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterAndItsParametersToAssignProperties()
{
// Arrange
var source = GetSourceText(new SourceGeneratorOptions(
true,
PropertyBuilder: builder =>
{
builder
.PadLeft(Indent2).AppendLine("[MapTypeConverter(typeof(Prop4Converter), new object[]{\"G\", 'C', 10})]")
.PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }");
},
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public long Prop4 { get; set; }")));
source += @"
namespace Test
{
using MapTo;
public class Prop4Converter: ITypeConverter<long, string>
{
public string Convert(long source, object[] converterParameters) => source.ToString(converterParameters[0] as string);
}
}
";
const string expectedSyntax = "Prop4 = new Test.Prop4Converter().Convert(baz.Prop4, new object[] { \"G\", 'C', 10 });";
// Act
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
// Assert // Assert
diagnostics.ShouldBeSuccessful(); diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedSyntax); compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedSyntax);
@ -665,12 +702,12 @@ namespace Test
.OfType<PropertyDeclarationSyntax>() .OfType<PropertyDeclarationSyntax>()
.Single(p => p.Identifier.ValueText == targetPropertyName); .Single(p => p.Identifier.ValueText == targetPropertyName);
} }
private static IPropertySymbol GetSourcePropertySymbol(string propertyName, Compilation compilation, string targetClass = "Foo") private static IPropertySymbol GetSourcePropertySymbol(string propertyName, Compilation compilation, string targetClass = "Foo")
{ {
var syntaxTree = compilation.SyntaxTrees.First(); var syntaxTree = compilation.SyntaxTrees.First();
var propSyntax = GetPropertyDeclarationSyntax(syntaxTree, propertyName, targetClass); var propSyntax = GetPropertyDeclarationSyntax(syntaxTree, propertyName, targetClass);
var semanticModel = compilation.GetSemanticModel(syntaxTree); var semanticModel = compilation.GetSemanticModel(syntaxTree);
return semanticModel.GetDeclaredSymbol(propSyntax); return semanticModel.GetDeclaredSymbol(propSyntax);
} }

View File

@ -16,7 +16,7 @@ namespace TestConsoleApp.ViewModels
private class LastNameConverter : ITypeConverter<long, string> private class LastNameConverter : ITypeConverter<long, string>
{ {
/// <inheritdoc /> /// <inheritdoc />
public string Convert(long source) => $"{source} :: With Type Converter"; public string Convert(long source, object[] converterParameters) => $"{source} :: With Type Converter";
} }
} }
} }