Use same access modifier as the declaring class.
This commit is contained in:
parent
c7d6d3ff27
commit
f7ff6e4793
|
@ -9,7 +9,7 @@ namespace MapTo.Extensions
|
||||||
|
|
||||||
internal static T GetBuildGlobalOption<T>(this GeneratorExecutionContext context, string propertyName, T defaultValue = default!) where T: notnull
|
internal static T GetBuildGlobalOption<T>(this GeneratorExecutionContext context, string propertyName, T defaultValue = default!) where T: notnull
|
||||||
{
|
{
|
||||||
if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue($"build_property.{PropertyNameSuffix}{propertyName}", out var optionValue))
|
if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(GetBuildPropertyName(propertyName), out var optionValue))
|
||||||
{
|
{
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -31,5 +31,7 @@ namespace MapTo.Extensions
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static string GetBuildPropertyName(string propertyName) => $"build_property.{PropertyNameSuffix}{propertyName}";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,87 +30,27 @@ namespace MapTo
|
||||||
AddGeneratedMappingsClasses(context, receiver.CandidateClasses, options);
|
AddGeneratedMappingsClasses(context, receiver.CandidateClasses, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddAttribute(GeneratorExecutionContext context, (string source, string hintName) attribute)
|
||||||
|
=> context.AddSource(attribute.hintName, attribute.source);
|
||||||
|
|
||||||
private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, IEnumerable<ClassDeclarationSyntax> candidateClasses, SourceGenerationOptions options)
|
private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, IEnumerable<ClassDeclarationSyntax> candidateClasses, SourceGenerationOptions options)
|
||||||
{
|
{
|
||||||
foreach (var classSyntax in candidateClasses)
|
foreach (var classSyntax in candidateClasses)
|
||||||
{
|
{
|
||||||
var model = CreateModel(context, classSyntax, options);
|
var classSemanticModel = context.Compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
||||||
|
var (model, diagnostics) = MapModel.CreateModel(classSemanticModel, classSyntax, options);
|
||||||
|
|
||||||
|
diagnostics.ForEach(context.ReportDiagnostic);
|
||||||
|
|
||||||
if (model is null)
|
if (model is null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
||||||
|
|
||||||
context.AddSource(hintName, source);
|
context.AddSource(hintName, source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddAttribute(GeneratorExecutionContext context, (string source, string hintName) attribute)
|
|
||||||
=> context.AddSource(attribute.hintName, attribute.source);
|
|
||||||
|
|
||||||
private static INamedTypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model)
|
|
||||||
{
|
|
||||||
var sourceTypeExpressionSyntax = classSyntax
|
|
||||||
.GetAttribute(SourceBuilder.MapFromAttributeName)
|
|
||||||
?.DescendantNodes()
|
|
||||||
.OfType<TypeOfExpressionSyntax>()
|
|
||||||
.SingleOrDefault();
|
|
||||||
|
|
||||||
return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type as INamedTypeSymbol : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MapModel? CreateModel(GeneratorExecutionContext context, ClassDeclarationSyntax classSyntax, SourceGenerationOptions sourceGenerationOptions)
|
|
||||||
{
|
|
||||||
var root = classSyntax.GetCompilationUnit();
|
|
||||||
var classSemanticModel = context.Compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
|
||||||
|
|
||||||
if (!(classSemanticModel.GetDeclaredSymbol(classSyntax) is INamedTypeSymbol classSymbol))
|
|
||||||
{
|
|
||||||
context.ReportDiagnostic(Diagnostics.SymbolNotFoundError(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceTypeSymbol = GetSourceTypeSymbol(classSyntax, classSemanticModel);
|
|
||||||
if (sourceTypeSymbol is null)
|
|
||||||
{
|
|
||||||
context.ReportDiagnostic(Diagnostics.MapFromAttributeNotFoundError(classSyntax.GetLocation()));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var className = classSyntax.GetClassName();
|
|
||||||
var sourceClassName = sourceTypeSymbol.Name;
|
|
||||||
|
|
||||||
var mappedProperties = GetMappedProperties(classSymbol, sourceTypeSymbol);
|
|
||||||
if (!mappedProperties.Any())
|
|
||||||
{
|
|
||||||
context.ReportDiagnostic(Diagnostics.NoMatchingPropertyFoundError(classSyntax.GetLocation(), className, sourceClassName));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MapModel(
|
|
||||||
sourceGenerationOptions,
|
|
||||||
root.GetNamespace(),
|
|
||||||
classSyntax.Modifiers,
|
|
||||||
className,
|
|
||||||
sourceTypeSymbol.ContainingNamespace.ToString(),
|
|
||||||
sourceClassName,
|
|
||||||
sourceTypeSymbol.ToString(),
|
|
||||||
mappedProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ImmutableArray<string> GetMappedProperties(ITypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
|
||||||
{
|
|
||||||
return sourceTypeSymbol
|
|
||||||
.GetAllMembersOfType<IPropertySymbol>()
|
|
||||||
.Select(p => (p.Name, p.Type.ToString()))
|
|
||||||
.Intersect(classSymbol
|
|
||||||
.GetAllMembersOfType<IPropertySymbol>()
|
|
||||||
.Where(p => p.GetAttributes().All(a => a.AttributeClass?.Name != SourceBuilder.IgnorePropertyAttributeName))
|
|
||||||
.Select(p => (p.Name, p.Type.ToString())))
|
|
||||||
.Select(p => p.Name)
|
|
||||||
.ToImmutableArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using MapTo.Extensions;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
namespace MapTo.Models
|
namespace MapTo.Models
|
||||||
{
|
{
|
||||||
|
@ -12,5 +16,74 @@ namespace MapTo.Models
|
||||||
string SourceClassName,
|
string SourceClassName,
|
||||||
string SourceClassFullName,
|
string SourceClassFullName,
|
||||||
ImmutableArray<string> MappedProperties
|
ImmutableArray<string> MappedProperties
|
||||||
);
|
)
|
||||||
|
{
|
||||||
|
internal static (MapModel? model, IEnumerable<Diagnostic> diagnostics) CreateModel(
|
||||||
|
SemanticModel classSemanticModel,
|
||||||
|
ClassDeclarationSyntax classSyntax,
|
||||||
|
SourceGenerationOptions sourceGenerationOptions)
|
||||||
|
{
|
||||||
|
var diagnostics = new List<Diagnostic>();
|
||||||
|
var root = classSyntax.GetCompilationUnit();
|
||||||
|
|
||||||
|
if (!(classSemanticModel.GetDeclaredSymbol(classSyntax) is INamedTypeSymbol classSymbol))
|
||||||
|
{
|
||||||
|
diagnostics.Add(Diagnostics.SymbolNotFoundError(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
||||||
|
return (default, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceTypeSymbol = GetSourceTypeSymbol(classSyntax, classSemanticModel);
|
||||||
|
if (sourceTypeSymbol is null)
|
||||||
|
{
|
||||||
|
diagnostics.Add(Diagnostics.MapFromAttributeNotFoundError(classSyntax.GetLocation()));
|
||||||
|
return (default, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
var className = classSyntax.GetClassName();
|
||||||
|
var sourceClassName = sourceTypeSymbol.Name;
|
||||||
|
|
||||||
|
var mappedProperties = GetMappedProperties(classSymbol, sourceTypeSymbol);
|
||||||
|
if (!mappedProperties.Any())
|
||||||
|
{
|
||||||
|
diagnostics.Add(Diagnostics.NoMatchingPropertyFoundError(classSyntax.GetLocation(), className, sourceClassName));
|
||||||
|
return (default, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = new MapModel(
|
||||||
|
sourceGenerationOptions,
|
||||||
|
root.GetNamespace(),
|
||||||
|
classSyntax.Modifiers,
|
||||||
|
className,
|
||||||
|
sourceTypeSymbol.ContainingNamespace.ToString(),
|
||||||
|
sourceClassName,
|
||||||
|
sourceTypeSymbol.ToString(),
|
||||||
|
mappedProperties);
|
||||||
|
|
||||||
|
return (model, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INamedTypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model)
|
||||||
|
{
|
||||||
|
var sourceTypeExpressionSyntax = classSyntax
|
||||||
|
.GetAttribute(SourceBuilder.MapFromAttributeName)
|
||||||
|
?.DescendantNodes()
|
||||||
|
.OfType<TypeOfExpressionSyntax>()
|
||||||
|
.SingleOrDefault();
|
||||||
|
|
||||||
|
return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type as INamedTypeSymbol : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableArray<string> GetMappedProperties(ITypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
||||||
|
{
|
||||||
|
return sourceTypeSymbol
|
||||||
|
.GetAllMembersOfType<IPropertySymbol>()
|
||||||
|
.Select(p => (p.Name, p.Type.ToString()))
|
||||||
|
.Intersect(classSymbol
|
||||||
|
.GetAllMembersOfType<IPropertySymbol>()
|
||||||
|
.Where(p => p.GetAttributes().All(a => a.AttributeClass?.Name != SourceBuilder.IgnorePropertyAttributeName))
|
||||||
|
.Select(p => (p.Name, p.Type.ToString())))
|
||||||
|
.Select(p => p.Name)
|
||||||
|
.ToImmutableArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Linq;
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
using MapTo.Extensions;
|
using MapTo.Extensions;
|
||||||
using MapTo.Models;
|
using MapTo.Models;
|
||||||
|
|
||||||
|
@ -111,7 +110,7 @@ namespace MapTo
|
||||||
|
|
||||||
// Class declaration
|
// Class declaration
|
||||||
.PadLeft(Indent1)
|
.PadLeft(Indent1)
|
||||||
.AppendFormat("{0} class {1}", model.ClassModifiers.ToFullString().Trim(), model.ClassName)
|
.AppendFormat("partial class {0}", model.ClassName)
|
||||||
.AppendOpeningBracket(Indent1)
|
.AppendOpeningBracket(Indent1)
|
||||||
|
|
||||||
// Class body
|
// Class body
|
||||||
|
@ -126,7 +125,7 @@ namespace MapTo
|
||||||
.AppendLine()
|
.AppendLine()
|
||||||
.AppendLine()
|
.AppendLine()
|
||||||
.PadLeft(Indent1)
|
.PadLeft(Indent1)
|
||||||
.AppendFormat("{0} static partial class {1}To{2}Extensions", model.ClassModifiers.FirstOrDefault().ToFullString().Trim(), model.SourceClassName, model.ClassName)
|
.AppendFormat("{0} static partial class {1}To{2}Extensions", model.Options.GeneratedMethodsAccessModifier.ToLowercaseString(), model.SourceClassName, model.ClassName)
|
||||||
.AppendOpeningBracket(Indent1)
|
.AppendOpeningBracket(Indent1)
|
||||||
|
|
||||||
// Extension class body
|
// Extension class body
|
||||||
|
|
|
@ -8,6 +8,7 @@ using MapTo.Tests.Infrastructure;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using static MapTo.Extensions.GeneratorExecutionContextExtensions;
|
||||||
|
|
||||||
namespace MapTo.Tests
|
namespace MapTo.Tests
|
||||||
{
|
{
|
||||||
|
@ -19,9 +20,9 @@ namespace MapTo.Tests
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> DefaultAnalyzerOptions = new()
|
private static readonly Dictionary<string, string> DefaultAnalyzerOptions = new()
|
||||||
{
|
{
|
||||||
["build_property.MapTo_GenerateXmlDocument"] = "false"
|
[GetBuildPropertyName(nameof(SourceGenerationOptions.GenerateXmlDocument))] = "false"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly string ExpectedAttribute = $@"{SourceBuilder.GeneratedFilesHeader}
|
private static readonly string ExpectedAttribute = $@"{SourceBuilder.GeneratedFilesHeader}
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
@ -40,20 +41,20 @@ namespace MapTo
|
||||||
}}";
|
}}";
|
||||||
|
|
||||||
private record SourceGeneratorOptions(
|
private record SourceGeneratorOptions(
|
||||||
bool UseMapToNamespace = false,
|
bool UseMapToNamespace = false,
|
||||||
string SourceClassNamespace = "Test.Models",
|
string SourceClassNamespace = "Test.Models",
|
||||||
int ClassPropertiesCount = 3,
|
int ClassPropertiesCount = 3,
|
||||||
int SourceClassPropertiesCount = 3,
|
int SourceClassPropertiesCount = 3,
|
||||||
Action<StringBuilder> PropertyBuilder = null,
|
Action<StringBuilder> PropertyBuilder = null,
|
||||||
Action<StringBuilder> SourcePropertyBuilder = null);
|
Action<StringBuilder> SourcePropertyBuilder = null);
|
||||||
|
|
||||||
private static string GetSourceText(SourceGeneratorOptions options = null)
|
private static string GetSourceText(SourceGeneratorOptions options = null)
|
||||||
{
|
{
|
||||||
const string ns = "Test";
|
const string ns = "Test";
|
||||||
options ??= new SourceGeneratorOptions();
|
options ??= new SourceGeneratorOptions();
|
||||||
var hasDifferentSourceNamespace = options.SourceClassNamespace != ns;
|
var hasDifferentSourceNamespace = options.SourceClassNamespace != ns;
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
if (options.UseMapToNamespace)
|
if (options.UseMapToNamespace)
|
||||||
{
|
{
|
||||||
builder.AppendFormat("using {0};", SourceBuilder.NamespaceName).AppendLine();
|
builder.AppendFormat("using {0};", SourceBuilder.NamespaceName).AppendLine();
|
||||||
|
@ -76,7 +77,7 @@ namespace MapTo
|
||||||
.AppendLine()
|
.AppendLine()
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.PadLeft(Indent1)
|
.PadLeft(Indent1)
|
||||||
.AppendLine(options.UseMapToNamespace ? "[MapTo.MapFrom(typeof(Baz))]" : "[MapFrom(typeof(Baz))]")
|
.AppendLine(options.UseMapToNamespace ? "[MapTo.MapFrom(typeof(Baz))]" : "[MapFrom(typeof(Baz))]")
|
||||||
|
@ -89,15 +90,15 @@ namespace MapTo
|
||||||
.PadLeft(Indent2)
|
.PadLeft(Indent2)
|
||||||
.AppendLine(i % 2 == 0 ? $"public int Prop{i} {{ get; set; }}" : $"public int Prop{i} {{ get; }}");
|
.AppendLine(i % 2 == 0 ? $"public int Prop{i} {{ get; set; }}" : $"public int Prop{i} {{ get; }}");
|
||||||
}
|
}
|
||||||
|
|
||||||
options.PropertyBuilder?.Invoke(builder);
|
options.PropertyBuilder?.Invoke(builder);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.AppendClosingBracket(Indent1, padNewLine: false)
|
.AppendClosingBracket(Indent1, false)
|
||||||
.AppendClosingBracket()
|
.AppendClosingBracket()
|
||||||
.AppendLine()
|
.AppendLine()
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.AppendFormat("namespace {0}", options.SourceClassNamespace)
|
.AppendFormat("namespace {0}", options.SourceClassNamespace)
|
||||||
.AppendOpeningBracket()
|
.AppendOpeningBracket()
|
||||||
|
@ -110,16 +111,40 @@ namespace MapTo
|
||||||
.PadLeft(Indent2)
|
.PadLeft(Indent2)
|
||||||
.AppendLine(i % 2 == 0 ? $"public int Prop{i} {{ get; set; }}" : $"public int Prop{i} {{ get; }}");
|
.AppendLine(i % 2 == 0 ? $"public int Prop{i} {{ get; set; }}" : $"public int Prop{i} {{ get; }}");
|
||||||
}
|
}
|
||||||
|
|
||||||
options.SourcePropertyBuilder?.Invoke(builder);
|
options.SourcePropertyBuilder?.Invoke(builder);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.AppendClosingBracket(Indent1, padNewLine: false)
|
.AppendClosingBracket(Indent1, false)
|
||||||
.AppendClosingBracket();
|
.AppendClosingBracket();
|
||||||
|
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VerifyIgnorePropertyAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string source = "";
|
||||||
|
var expectedAttribute = $@"
|
||||||
|
{SourceBuilder.GeneratedFilesHeader}
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MapTo
|
||||||
|
{{
|
||||||
|
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
|
||||||
|
public sealed class IgnorePropertyAttribute : Attribute {{ }}
|
||||||
|
}}
|
||||||
|
".Trim();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.ShouldContain(c => c.ToString() == expectedAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyMapToAttribute()
|
public void VerifyMapToAttribute()
|
||||||
{
|
{
|
||||||
|
@ -134,6 +159,112 @@ namespace MapTo
|
||||||
compilation.SyntaxTrees.ShouldContain(c => c.ToString() == ExpectedAttribute);
|
compilation.SyntaxTrees.ShouldContain(c => c.ToString() == ExpectedAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_FoundMatchingPropertyNameWithDifferentType_Should_Ignore()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText(new SourceGeneratorOptions(
|
||||||
|
true,
|
||||||
|
PropertyBuilder: builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }");
|
||||||
|
},
|
||||||
|
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }")));
|
||||||
|
|
||||||
|
var expectedResult = @"
|
||||||
|
partial class Foo
|
||||||
|
{
|
||||||
|
public Foo(Test.Models.Baz baz)
|
||||||
|
{
|
||||||
|
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||||
|
|
||||||
|
Prop1 = baz.Prop1;
|
||||||
|
Prop2 = baz.Prop2;
|
||||||
|
Prop3 = baz.Prop3;
|
||||||
|
}
|
||||||
|
".Trim();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_IgnorePropertyAttributeIsSpecified_Should_NotGenerateMappingsForThatProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText(new SourceGeneratorOptions(
|
||||||
|
true,
|
||||||
|
PropertyBuilder: builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.PadLeft(Indent2).AppendLine("[IgnoreProperty]")
|
||||||
|
.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }");
|
||||||
|
},
|
||||||
|
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }")));
|
||||||
|
|
||||||
|
var expectedResult = @"
|
||||||
|
partial class Foo
|
||||||
|
{
|
||||||
|
public Foo(Test.Models.Baz baz)
|
||||||
|
{
|
||||||
|
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||||
|
|
||||||
|
Prop1 = baz.Prop1;
|
||||||
|
Prop2 = baz.Prop2;
|
||||||
|
Prop3 = baz.Prop3;
|
||||||
|
}
|
||||||
|
".Trim();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_MappingsModifierOptionIsSetToInternal_Should_GenerateThoseMethodsWithInternalAccessModifier()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText();
|
||||||
|
var configOptions = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
[GetBuildPropertyName(nameof(SourceGenerationOptions.GeneratedMethodsAccessModifier))] = "Internal",
|
||||||
|
[GetBuildPropertyName(nameof(SourceGenerationOptions.GenerateXmlDocument))] = "false"
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedExtension = @"
|
||||||
|
internal static partial class BazToFooExtensions
|
||||||
|
{
|
||||||
|
internal static Foo ToFoo(this Test.Models.Baz baz)
|
||||||
|
{
|
||||||
|
return baz == null ? null : new Foo(baz);
|
||||||
|
}
|
||||||
|
}".Trim();
|
||||||
|
|
||||||
|
var expectedFactory = @"
|
||||||
|
internal static Foo From(Test.Models.Baz baz)
|
||||||
|
{
|
||||||
|
return baz == null ? null : new Foo(baz);
|
||||||
|
}".Trim();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: configOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
|
||||||
|
var syntaxTree = compilation.SyntaxTrees.Last().ToString();
|
||||||
|
syntaxTree.ShouldContain(expectedFactory);
|
||||||
|
syntaxTree.ShouldContain(expectedExtension);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void When_MapToAttributeFound_Should_GenerateTheClass()
|
public void When_MapToAttributeFound_Should_GenerateTheClass()
|
||||||
{
|
{
|
||||||
|
@ -162,7 +293,7 @@ using System;
|
||||||
|
|
||||||
namespace Test
|
namespace Test
|
||||||
{
|
{
|
||||||
public partial class Foo
|
partial class Foo
|
||||||
{
|
{
|
||||||
public Foo(Test.Baz baz)
|
public Foo(Test.Baz baz)
|
||||||
{
|
{
|
||||||
|
@ -179,7 +310,7 @@ namespace Test
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void When_MapToAttributeFoundWithoutMatchingProperties_Should_ReportError()
|
public void When_MapToAttributeFoundWithoutMatchingProperties_Should_ReportError()
|
||||||
{
|
{
|
||||||
|
@ -225,7 +356,7 @@ using System;
|
||||||
|
|
||||||
namespace Test
|
namespace Test
|
||||||
{
|
{
|
||||||
public partial class Foo
|
partial class Foo
|
||||||
{
|
{
|
||||||
public Foo(Test.Baz baz)
|
public Foo(Test.Baz baz)
|
||||||
{
|
{
|
||||||
|
@ -289,7 +420,7 @@ namespace Test
|
||||||
var source = GetSourceText();
|
var source = GetSourceText();
|
||||||
|
|
||||||
const string expectedResult = @"
|
const string expectedResult = @"
|
||||||
public partial class Foo
|
partial class Foo
|
||||||
{
|
{
|
||||||
public Foo(Test.Models.Baz baz)
|
public Foo(Test.Models.Baz baz)
|
||||||
{
|
{
|
||||||
|
@ -353,131 +484,5 @@ namespace Test
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void VerifyIgnorePropertyAttribute()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string source = "";
|
|
||||||
var expectedAttribute = $@"
|
|
||||||
{SourceBuilder.GeneratedFilesHeader}
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MapTo
|
|
||||||
{{
|
|
||||||
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
|
|
||||||
public sealed class IgnorePropertyAttribute : Attribute {{ }}
|
|
||||||
}}
|
|
||||||
".Trim();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.ShouldContain(c => c.ToString() == expectedAttribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_IgnorePropertyAttributeIsSpecified_Should_NotGenerateMappingsForThatProperty()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var source = GetSourceText(new SourceGeneratorOptions(
|
|
||||||
UseMapToNamespace: true,
|
|
||||||
PropertyBuilder: builder =>
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.PadLeft(Indent2).AppendLine("[IgnoreProperty]")
|
|
||||||
.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }");
|
|
||||||
},
|
|
||||||
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }")));
|
|
||||||
|
|
||||||
var expectedResult = @"
|
|
||||||
public partial class Foo
|
|
||||||
{
|
|
||||||
public Foo(Test.Models.Baz baz)
|
|
||||||
{
|
|
||||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
|
||||||
|
|
||||||
Prop1 = baz.Prop1;
|
|
||||||
Prop2 = baz.Prop2;
|
|
||||||
Prop3 = baz.Prop3;
|
|
||||||
}
|
|
||||||
".Trim();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_FoundMatchingPropertyNameWithDifferentType_Should_Ignore()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var source = GetSourceText(new SourceGeneratorOptions(
|
|
||||||
UseMapToNamespace: true,
|
|
||||||
PropertyBuilder: builder =>
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }");
|
|
||||||
},
|
|
||||||
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }")));
|
|
||||||
|
|
||||||
var expectedResult = @"
|
|
||||||
public partial class Foo
|
|
||||||
{
|
|
||||||
public Foo(Test.Models.Baz baz)
|
|
||||||
{
|
|
||||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
|
||||||
|
|
||||||
Prop1 = baz.Prop1;
|
|
||||||
Prop2 = baz.Prop2;
|
|
||||||
Prop3 = baz.Prop3;
|
|
||||||
}
|
|
||||||
".Trim();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_MappingsModifierOptionIsSetToInternal_Should_GenerateThoseMethodsWithInternalAccessModifier()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var source = GetSourceText();
|
|
||||||
var configOptions = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
[$"build_property.MapTo_{nameof(SourceGenerationOptions.GeneratedMethodsAccessModifier)}"] = "Internal"
|
|
||||||
};
|
|
||||||
|
|
||||||
var expectedExtension = @"
|
|
||||||
internal static Foo ToFoo(this Test.Models.Baz baz)
|
|
||||||
{
|
|
||||||
return baz == null ? null : new Foo(baz);
|
|
||||||
}".Trim();
|
|
||||||
|
|
||||||
var expectedFactory = @"
|
|
||||||
internal static Foo From(Test.Models.Baz baz)
|
|
||||||
{
|
|
||||||
return baz == null ? null : new Foo(baz);
|
|
||||||
}".Trim();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: configOptions);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
|
|
||||||
var syntaxTree = compilation.SyntaxTrees.Last().ToString();
|
|
||||||
syntaxTree.ShouldContain(expectedFactory);
|
|
||||||
syntaxTree.ShouldContain(expectedExtension);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue