Add option to generate xml docs.
This commit is contained in:
parent
09d09430ef
commit
85d47d67c3
|
@ -1,26 +0,0 @@
|
||||||
using System;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace MapTo.Configuration
|
|
||||||
{
|
|
||||||
internal sealed class MapToConfigurations
|
|
||||||
{
|
|
||||||
private MapToConfigurations(AccessModifier constructorAccessModifier)
|
|
||||||
{
|
|
||||||
ConstructorAccessModifier = constructorAccessModifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AccessModifier ConstructorAccessModifier { get; }
|
|
||||||
|
|
||||||
internal static MapToConfigurations From(GeneratorExecutionContext context)
|
|
||||||
{
|
|
||||||
var constructorAccessModifier =
|
|
||||||
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.MapTo_ConstructorAccessModifier", out var ctorModifierValue) &&
|
|
||||||
Enum.TryParse<AccessModifier>(ctorModifierValue, out var ctorModifier) ? ctorModifier : AccessModifier.Public;
|
|
||||||
|
|
||||||
return new MapToConfigurations(
|
|
||||||
constructorAccessModifier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,18 +5,21 @@ namespace MapTo
|
||||||
internal static class Diagnostics
|
internal static class Diagnostics
|
||||||
{
|
{
|
||||||
private const string UsageCategory = "Usage";
|
private const string UsageCategory = "Usage";
|
||||||
|
private const string ErrorId = "MT0";
|
||||||
|
private const string InfoId = "MT1";
|
||||||
|
private const string WarningId = "MT2";
|
||||||
|
|
||||||
internal static Diagnostic SymbolNotFoundError(Location location, string syntaxName) =>
|
internal static Diagnostic SymbolNotFoundError(Location location, string syntaxName) =>
|
||||||
Create("MT0001", "Symbol not found.", $"Unable to find any symbols for {syntaxName}", location);
|
Create($"{ErrorId}001", "Symbol not found.", $"Unable to find any symbols for {syntaxName}", location);
|
||||||
|
|
||||||
internal static Diagnostic MapFromAttributeNotFoundError(Location location) =>
|
internal static Diagnostic MapFromAttributeNotFoundError(Location location) =>
|
||||||
Create("MT0002", "Attribute Not Available", $"Unable to find {SourceBuilder.MapFromAttributeName} type.", location);
|
Create($"{ErrorId}002", "Attribute Not Available", $"Unable to find {SourceBuilder.MapFromAttributeName} type.", location);
|
||||||
|
|
||||||
internal static Diagnostic ClassMappingsGenerated(Location location, string typeName) =>
|
|
||||||
Create("MT1001", "Mapped Type", $"Generated mappings for {typeName}", location, DiagnosticSeverity.Info);
|
|
||||||
|
|
||||||
internal static Diagnostic NoMatchingPropertyFoundError(Location location, string className, string sourceTypeName) =>
|
internal static Diagnostic NoMatchingPropertyFoundError(Location location, string className, string sourceTypeName) =>
|
||||||
Create("MT2001", "Property Not Found", $"No matching properties found between '{className}' and '{sourceTypeName}' types.", location);
|
Create($"{ErrorId}003", "Property Not Found", $"No matching properties found between '{className}' and '{sourceTypeName}' types.", location);
|
||||||
|
|
||||||
|
internal static Diagnostic ConfigurationParseError(string error) =>
|
||||||
|
Create($"{ErrorId}004", "Incorrect Configuration", error, Location.None);
|
||||||
|
|
||||||
private static Diagnostic Create(string id, string title, string message, Location location, DiagnosticSeverity severity = DiagnosticSeverity.Error) =>
|
private static Diagnostic Create(string id, string title, string message, Location location, DiagnosticSeverity severity = DiagnosticSeverity.Error) =>
|
||||||
Diagnostic.Create(new DiagnosticDescriptor(id, title, message, UsageCategory, severity, true), location);
|
Diagnostic.Create(new DiagnosticDescriptor(id, title, message, UsageCategory, severity, true), location);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MapTo.Extensions
|
||||||
|
{
|
||||||
|
internal static class EnumExtensions
|
||||||
|
{
|
||||||
|
internal static string ToLowercaseString(this Enum member) => member.ToString().ToLower();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace MapTo.Extensions
|
||||||
|
{
|
||||||
|
internal static class GeneratorExecutionContextExtensions
|
||||||
|
{
|
||||||
|
private const string PropertyNameSuffix = "MapTo_";
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = typeof(T);
|
||||||
|
|
||||||
|
if (!type.IsEnum)
|
||||||
|
{
|
||||||
|
return (T)Convert.ChangeType(optionValue, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (T)Enum.Parse(type, optionValue, true);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(Diagnostics.ConfigurationParseError($"'{optionValue}' is not a valid value for {PropertyNameSuffix}{propertyName} property."));
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MapTo.Configuration;
|
|
||||||
using MapTo.Extensions;
|
using MapTo.Extensions;
|
||||||
using MapTo.Models;
|
using MapTo.Models;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
@ -21,23 +20,22 @@ namespace MapTo
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Execute(GeneratorExecutionContext context)
|
public void Execute(GeneratorExecutionContext context)
|
||||||
{
|
{
|
||||||
AddAttribute(context, SourceBuilder.GenerateMapFromAttribute());
|
var options = SourceGenerationOptions.From(context);
|
||||||
AddAttribute(context, SourceBuilder.GenerateIgnorePropertyAttribute());
|
|
||||||
|
AddAttribute(context, SourceBuilder.GenerateMapFromAttribute(options));
|
||||||
|
AddAttribute(context, SourceBuilder.GenerateIgnorePropertyAttribute(options));
|
||||||
|
|
||||||
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())
|
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())
|
||||||
{
|
{
|
||||||
AddGeneratedMappingsClasses(context, receiver.CandidateClasses);
|
AddGeneratedMappingsClasses(context, receiver.CandidateClasses, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, IEnumerable<ClassDeclarationSyntax> candidateClasses)
|
private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, IEnumerable<ClassDeclarationSyntax> candidateClasses, SourceGenerationOptions options)
|
||||||
{
|
{
|
||||||
var configs = MapToConfigurations.From(context);
|
|
||||||
|
|
||||||
foreach (var classSyntax in candidateClasses)
|
foreach (var classSyntax in candidateClasses)
|
||||||
{
|
{
|
||||||
|
var model = CreateModel(context, classSyntax, options);
|
||||||
var model = CreateModel(context, classSyntax, configs);
|
|
||||||
if (model is null)
|
if (model is null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -46,7 +44,6 @@ namespace MapTo
|
||||||
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
||||||
|
|
||||||
context.AddSource(hintName, source);
|
context.AddSource(hintName, source);
|
||||||
context.ReportDiagnostic(Diagnostics.ClassMappingsGenerated(classSyntax.GetLocation(), model.ClassName));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +61,7 @@ namespace MapTo
|
||||||
return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type as INamedTypeSymbol : null;
|
return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type as INamedTypeSymbol : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MapModel? CreateModel(GeneratorExecutionContext context, ClassDeclarationSyntax classSyntax, MapToConfigurations configs)
|
private static MapModel? CreateModel(GeneratorExecutionContext context, ClassDeclarationSyntax classSyntax, SourceGenerationOptions sourceGenerationOptions)
|
||||||
{
|
{
|
||||||
var root = classSyntax.GetCompilationUnit();
|
var root = classSyntax.GetCompilationUnit();
|
||||||
var classSemanticModel = context.Compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
var classSemanticModel = context.Compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
||||||
|
@ -93,14 +90,14 @@ namespace MapTo
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MapModel(
|
return new MapModel(
|
||||||
|
sourceGenerationOptions,
|
||||||
root.GetNamespace(),
|
root.GetNamespace(),
|
||||||
classSyntax.Modifiers,
|
classSyntax.Modifiers,
|
||||||
className,
|
className,
|
||||||
sourceTypeSymbol.ContainingNamespace.ToString(),
|
sourceTypeSymbol.ContainingNamespace.ToString(),
|
||||||
sourceClassName,
|
sourceClassName,
|
||||||
sourceTypeSymbol.ToString(),
|
sourceTypeSymbol.ToString(),
|
||||||
mappedProperties,
|
mappedProperties);
|
||||||
configs.ConstructorAccessModifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableArray<string> GetMappedProperties(ITypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
private static ImmutableArray<string> GetMappedProperties(ITypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace MapTo.Configuration
|
namespace MapTo.Models
|
||||||
{
|
{
|
||||||
internal enum AccessModifier
|
internal enum AccessModifier
|
||||||
{
|
{
|
|
@ -1,45 +1,16 @@
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MapTo.Configuration;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace MapTo.Models
|
namespace MapTo.Models
|
||||||
{
|
{
|
||||||
internal class MapModel
|
internal record MapModel (
|
||||||
{
|
SourceGenerationOptions Options,
|
||||||
internal MapModel(
|
string? Namespace,
|
||||||
string? ns,
|
SyntaxTokenList ClassModifiers,
|
||||||
SyntaxTokenList classModifiers,
|
string ClassName,
|
||||||
string className,
|
string SourceNamespace,
|
||||||
string sourceNamespace,
|
string SourceClassName,
|
||||||
string sourceClassName,
|
string SourceClassFullName,
|
||||||
string sourceClassFullName,
|
ImmutableArray<string> MappedProperties
|
||||||
ImmutableArray<string> mappedProperties,
|
);
|
||||||
AccessModifier constructorAccessModifier)
|
|
||||||
{
|
|
||||||
Namespace = ns;
|
|
||||||
ClassModifiers = classModifiers;
|
|
||||||
ClassName = className;
|
|
||||||
SourceNamespace = sourceNamespace;
|
|
||||||
SourceClassName = sourceClassName;
|
|
||||||
SourceClassFullName = sourceClassFullName;
|
|
||||||
MappedProperties = mappedProperties;
|
|
||||||
ConstructorAccessModifier = constructorAccessModifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? Namespace { get; }
|
|
||||||
|
|
||||||
public SyntaxTokenList ClassModifiers { get; }
|
|
||||||
|
|
||||||
public string ClassName { get; }
|
|
||||||
|
|
||||||
public string SourceNamespace { get; }
|
|
||||||
|
|
||||||
public string SourceClassName { get; }
|
|
||||||
|
|
||||||
public string SourceClassFullName { get; }
|
|
||||||
|
|
||||||
public ImmutableArray<string> MappedProperties { get; }
|
|
||||||
|
|
||||||
public AccessModifier ConstructorAccessModifier { get; }
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
using MapTo.Extensions;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace MapTo.Models
|
||||||
|
{
|
||||||
|
internal record SourceGenerationOptions(
|
||||||
|
AccessModifier ConstructorAccessModifier,
|
||||||
|
AccessModifier MappingsAccessModifier,
|
||||||
|
bool GenerateXmlDocument)
|
||||||
|
{
|
||||||
|
internal static SourceGenerationOptions From(GeneratorExecutionContext context) => new(
|
||||||
|
context.GetBuildGlobalOption<AccessModifier>(nameof(ConstructorAccessModifier)),
|
||||||
|
context.GetBuildGlobalOption<AccessModifier>(nameof(MappingsAccessModifier)),
|
||||||
|
context.GetBuildGlobalOption(nameof(GenerateXmlDocument), defaultValue: true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,53 +17,85 @@ namespace MapTo
|
||||||
private const int Indent2 = Indent1 * 2;
|
private const int Indent2 = Indent1 * 2;
|
||||||
private const int Indent3 = Indent1 * 3;
|
private const int Indent3 = Indent1 * 3;
|
||||||
|
|
||||||
internal static (string source, string hintName) GenerateMapFromAttribute()
|
internal static (string source, string hintName) GenerateMapFromAttribute(SourceGenerationOptions options)
|
||||||
{
|
{
|
||||||
var source = $@"{GeneratedFilesHeader}
|
var builder = new StringBuilder();
|
||||||
using System;
|
builder
|
||||||
|
.AppendFileHeader()
|
||||||
|
.AppendLine("using System;")
|
||||||
|
.AppendLine()
|
||||||
|
.AppendFormat("namespace {0}", NamespaceName)
|
||||||
|
.AppendOpeningBracket();
|
||||||
|
|
||||||
|
if (options.GenerateXmlDocument)
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.PadLeft(Indent1).AppendLine("/// <summary>")
|
||||||
|
.PadLeft(Indent1).AppendLine("/// Specifies that the annotated class can be mapped from the provided <see cref=\"SourceType\"/>.")
|
||||||
|
.PadLeft(Indent1).AppendLine("/// </summary>");
|
||||||
|
}
|
||||||
|
|
||||||
namespace MapTo
|
builder
|
||||||
{{
|
.PadLeft(Indent1).AppendLine("[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]")
|
||||||
/// <summary>
|
.PadLeft(Indent1).AppendFormat("public sealed class {0}Attribute : Attribute", MapFromAttributeName)
|
||||||
/// Specifies that the annotated class can be mapped from the provided <see cref=""SourceType""/>.
|
.AppendOpeningBracket(Indent1);
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
|
||||||
public sealed class {MapFromAttributeName}Attribute : Attribute
|
|
||||||
{{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref=""{MapFromAttributeName}Attribute""/> class
|
|
||||||
/// with the specified <paramref name=""sourceType""/>.
|
|
||||||
/// </summary>
|
|
||||||
public {MapFromAttributeName}Attribute(Type sourceType)
|
|
||||||
{{
|
|
||||||
SourceType = sourceType;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/// <summary>
|
if (options.GenerateXmlDocument)
|
||||||
/// Gets the type of the class that the annotated class should be able to map from.
|
{
|
||||||
/// </summary>
|
builder
|
||||||
public Type SourceType {{ get; }}
|
.PadLeft(Indent2).AppendLine("/// <summary>")
|
||||||
}}
|
.PadLeft(Indent2).AppendFormat("/// Initializes a new instance of the <see cref=\"{0}Attribute\"/> class with the specified <paramref name=\"sourceType\"/>.", MapFromAttributeName).AppendLine()
|
||||||
}}";
|
.PadLeft(Indent2).AppendLine("/// </summary>");
|
||||||
|
}
|
||||||
|
|
||||||
return (source, $"{MapFromAttributeName}Attribute.g.cs");
|
builder
|
||||||
|
.PadLeft(Indent2).AppendFormat("public {0}Attribute(Type sourceType)", MapFromAttributeName)
|
||||||
|
.AppendOpeningBracket(Indent2)
|
||||||
|
.PadLeft(Indent3).AppendLine("SourceType = sourceType;")
|
||||||
|
.AppendClosingBracket(Indent2, padNewLine: false)
|
||||||
|
.AppendLine()
|
||||||
|
.AppendLine();
|
||||||
|
|
||||||
|
if (options.GenerateXmlDocument)
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.PadLeft(Indent2).AppendLine("/// <summary>")
|
||||||
|
.PadLeft(Indent2).AppendLine("/// Gets the type of the class that the annotated class should be able to map from.")
|
||||||
|
.PadLeft(Indent2).AppendLine("/// </summary>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
|
.PadLeft(Indent2).AppendLine("public Type SourceType { get; }")
|
||||||
|
.AppendClosingBracket(Indent1, padNewLine: false)
|
||||||
|
.AppendClosingBracket();
|
||||||
|
|
||||||
|
return (builder.ToString(), $"{MapFromAttributeName}Attribute.g.cs");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static (string source, string hintName) GenerateIgnorePropertyAttribute()
|
internal static (string source, string hintName) GenerateIgnorePropertyAttribute(SourceGenerationOptions options)
|
||||||
{
|
{
|
||||||
var source = $@"{GeneratedFilesHeader}
|
var builder = new StringBuilder();
|
||||||
using System;
|
builder
|
||||||
|
.AppendFileHeader()
|
||||||
|
.AppendLine("using System;")
|
||||||
|
.AppendLine()
|
||||||
|
.AppendFormat("namespace {0}", NamespaceName)
|
||||||
|
.AppendOpeningBracket();
|
||||||
|
|
||||||
namespace MapTo
|
if (options.GenerateXmlDocument)
|
||||||
{{
|
{
|
||||||
/// <summary>
|
builder
|
||||||
/// Specified that the annotated property should not be included in the generated mappings.
|
.PadLeft(Indent1).AppendLine("/// <summary>")
|
||||||
/// </summary>
|
.PadLeft(Indent1).AppendLine("/// Specified that the annotated property should not be included in the generated mappings.")
|
||||||
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
|
.PadLeft(Indent1).AppendLine("/// </summary>");
|
||||||
public sealed class {IgnorePropertyAttributeName}Attribute : Attribute {{ }}
|
}
|
||||||
}}";
|
|
||||||
|
|
||||||
return (source, $"{IgnorePropertyAttributeName}Attribute.g.cs");
|
builder
|
||||||
|
.PadLeft(Indent1).AppendLine("[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]")
|
||||||
|
.PadLeft(Indent1).AppendFormat("public sealed class {0}Attribute : Attribute {{ }}", IgnorePropertyAttributeName)
|
||||||
|
.AppendClosingBracket();
|
||||||
|
|
||||||
|
return (builder.ToString(), $"{IgnorePropertyAttributeName}Attribute.g.cs");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static (string source, string hintName) GenerateSource(MapModel model)
|
internal static (string source, string hintName) GenerateSource(MapModel model)
|
||||||
|
@ -116,23 +148,22 @@ namespace MapTo
|
||||||
return builder.AppendLine();
|
return builder.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builder"></param>
|
|
||||||
/// <param name="model"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static StringBuilder GenerateConstructor(this StringBuilder builder, MapModel model)
|
private static StringBuilder GenerateConstructor(this StringBuilder builder, MapModel model)
|
||||||
{
|
{
|
||||||
var sourceClassParameterName = model.SourceClassName.ToCamelCase();
|
var sourceClassParameterName = model.SourceClassName.ToCamelCase();
|
||||||
|
|
||||||
|
if (model.Options.GenerateXmlDocument)
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.PadLeft(Indent2).AppendLine("/// <summary>")
|
||||||
|
.PadLeft(Indent2).AppendFormat("/// Initializes a new instance of the <see cref=\"{0}\"/> class", model.ClassName).AppendLine()
|
||||||
|
.PadLeft(Indent2).AppendFormat("/// using the property values from the specified <paramref name=\"{0}\"/>.", sourceClassParameterName).AppendLine()
|
||||||
|
.PadLeft(Indent2).AppendLine("/// </summary>")
|
||||||
|
.PadLeft(Indent2).AppendFormat("/// <exception cref=\"ArgumentNullException\">{0} is null</exception>", sourceClassParameterName).AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.PadLeft(Indent2).AppendLine("/// <summary>")
|
.PadLeft(Indent2).AppendFormat("{0} {1}({2} {3})", model.Options.ConstructorAccessModifier.ToLowercaseString(), model.ClassName, model.SourceClassFullName, sourceClassParameterName)
|
||||||
.PadLeft(Indent2).AppendFormat("/// Initializes a new instance of the <see cref=\"{0}\"/> class", model.ClassName).AppendLine()
|
|
||||||
.PadLeft(Indent2).AppendFormat("/// using the property values from the specified <paramref name=\"{0}\"/>.", sourceClassParameterName).AppendLine()
|
|
||||||
.PadLeft(Indent2).AppendLine("/// </summary>")
|
|
||||||
.PadLeft(Indent2).AppendFormat("/// <exception cref=\"ArgumentNullException\">{0} is null</exception>", sourceClassParameterName).AppendLine()
|
|
||||||
.PadLeft(Indent2).AppendFormat("{0} {1}({2} {3})", model.ConstructorAccessModifier.ToString().ToLower(), model.ClassName, model.SourceClassFullName, sourceClassParameterName)
|
|
||||||
.AppendOpeningBracket(Indent2)
|
.AppendOpeningBracket(Indent2)
|
||||||
.PadLeft(Indent3).AppendFormat("if ({0} == null) throw new ArgumentNullException(nameof({0}));", sourceClassParameterName).AppendLine()
|
.PadLeft(Indent3).AppendFormat("if ({0} == null) throw new ArgumentNullException(nameof({0}));", sourceClassParameterName).AppendLine()
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
|
@ -149,13 +180,6 @@ namespace MapTo
|
||||||
return builder.AppendClosingBracket(Indent2, false);
|
return builder.AppendClosingBracket(Indent2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builder"></param>
|
|
||||||
/// <param name="model"></param>
|
|
||||||
/// <exception cref="ArgumentNullException"></exception>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static StringBuilder GenerateFactoryMethod(this StringBuilder builder, MapModel model)
|
private static StringBuilder GenerateFactoryMethod(this StringBuilder builder, MapModel model)
|
||||||
{
|
{
|
||||||
var sourceClassParameterName = model.SourceClassName.ToCamelCase();
|
var sourceClassParameterName = model.SourceClassName.ToCamelCase();
|
||||||
|
@ -186,6 +210,11 @@ namespace MapTo
|
||||||
|
|
||||||
private static StringBuilder AppendConvertorMethodsXmlDocs(this StringBuilder builder, MapModel model, string sourceClassParameterName)
|
private static StringBuilder AppendConvertorMethodsXmlDocs(this StringBuilder builder, MapModel model, string sourceClassParameterName)
|
||||||
{
|
{
|
||||||
|
if (!model.Options.GenerateXmlDocument)
|
||||||
|
{
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
.PadLeft(Indent2).AppendLine("/// <summary>")
|
.PadLeft(Indent2).AppendLine("/// <summary>")
|
||||||
.PadLeft(Indent2).AppendFormat("/// Creates a new instance of <see cref=\"{0}\"/> and sets its participating properties", model.ClassName).AppendLine()
|
.PadLeft(Indent2).AppendFormat("/// Creates a new instance of <see cref=\"{0}\"/> and sets its participating properties", model.ClassName).AppendLine()
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MapTo;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace MapToTests
|
namespace MapTo.Tests.Infrastructure
|
||||||
{
|
{
|
||||||
internal static class CSharpGenerator
|
internal static class CSharpGenerator
|
||||||
{
|
{
|
||||||
|
@ -16,7 +15,7 @@ namespace MapToTests
|
||||||
Assert.False(diagnostics.Any(d => d.Severity >= DiagnosticSeverity.Warning), $"Failed: {Environment.NewLine}{string.Join($"{Environment.NewLine}- ", diagnostics.Select(c => c.GetMessage()))}");
|
Assert.False(diagnostics.Any(d => d.Severity >= DiagnosticSeverity.Warning), $"Failed: {Environment.NewLine}{string.Join($"{Environment.NewLine}- ", diagnostics.Select(c => c.GetMessage()))}");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static (Compilation compilation, ImmutableArray<Diagnostic> diagnostics) GetOutputCompilation(string source, bool assertCompilation = false)
|
internal static (Compilation compilation, ImmutableArray<Diagnostic> diagnostics) GetOutputCompilation(string source, bool assertCompilation = false, IDictionary<string, string> analyzerConfigOptions = null)
|
||||||
{
|
{
|
||||||
var syntaxTree = CSharpSyntaxTree.ParseText(source);
|
var syntaxTree = CSharpSyntaxTree.ParseText(source);
|
||||||
var references = AppDomain.CurrentDomain.GetAssemblies()
|
var references = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
@ -33,10 +32,12 @@ namespace MapToTests
|
||||||
Assert.False(compileDiagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Failed: {Environment.NewLine}{string.Join($"{Environment.NewLine}- ", compileDiagnostics.Select(c => c.GetMessage()))}");
|
Assert.False(compileDiagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Failed: {Environment.NewLine}{string.Join($"{Environment.NewLine}- ", compileDiagnostics.Select(c => c.GetMessage()))}");
|
||||||
}
|
}
|
||||||
|
|
||||||
ISourceGenerator generator = new MapToGenerator();
|
var driver = CSharpGeneratorDriver.Create(
|
||||||
var driver = CSharpGeneratorDriver.Create(generator);
|
new[] { new MapToGenerator() },
|
||||||
driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var generateDiagnostics);
|
optionsProvider: new TestAnalyzerConfigOptionsProvider(analyzerConfigOptions)
|
||||||
|
);
|
||||||
|
|
||||||
|
driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var generateDiagnostics);
|
||||||
return (outputCompilation, generateDiagnostics);
|
return (outputCompilation, generateDiagnostics);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
namespace MapTo.Tests.Infrastructure
|
||||||
|
{
|
||||||
|
internal sealed class TestAnalyzerConfigOptions : AnalyzerConfigOptions
|
||||||
|
{
|
||||||
|
private readonly ImmutableDictionary<string, string> _backing;
|
||||||
|
|
||||||
|
public TestAnalyzerConfigOptions(IDictionary<string, string> properties)
|
||||||
|
{
|
||||||
|
_backing = properties?.ToImmutableDictionary(KeyComparer) ?? ImmutableDictionary.Create<string, string>(KeyComparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryGetValue(string key, [NotNullWhen(true)] out string value) => _backing.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
namespace MapTo.Tests.Infrastructure
|
||||||
|
{
|
||||||
|
internal sealed class TestAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider
|
||||||
|
{
|
||||||
|
public TestAnalyzerConfigOptionsProvider(IDictionary<string, string> options)
|
||||||
|
{
|
||||||
|
GlobalOptions = new TestAnalyzerConfigOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override AnalyzerConfigOptions GlobalOptions { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MapTo.Extensions;
|
using MapTo.Extensions;
|
||||||
using MapToTests;
|
using MapTo.Tests.Infrastructure;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@ -15,14 +16,12 @@ namespace MapTo.Tests
|
||||||
private const int Indent1 = 4;
|
private const int Indent1 = 4;
|
||||||
private const int Indent2 = Indent1 * 2;
|
private const int Indent2 = Indent1 * 2;
|
||||||
private const int Indent3 = Indent1 * 3;
|
private const int Indent3 = Indent1 * 3;
|
||||||
|
|
||||||
public Tests(ITestOutputHelper output)
|
private static readonly Dictionary<string, string> DefaultAnalyzerOptions = new()
|
||||||
{
|
{
|
||||||
_output = output;
|
["build_property.MapTo_GenerateXmlDocument"] = "false"
|
||||||
}
|
};
|
||||||
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
private static readonly string ExpectedAttribute = $@"{SourceBuilder.GeneratedFilesHeader}
|
private static readonly string ExpectedAttribute = $@"{SourceBuilder.GeneratedFilesHeader}
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
@ -128,7 +127,7 @@ namespace MapTo
|
||||||
const string source = "";
|
const string source = "";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
@ -174,7 +173,7 @@ namespace Test
|
||||||
";
|
";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
@ -237,7 +236,7 @@ namespace Test
|
||||||
";
|
";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
@ -303,7 +302,7 @@ namespace Test
|
||||||
";
|
";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
@ -348,7 +347,7 @@ namespace Test
|
||||||
";
|
";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
@ -372,7 +371,7 @@ namespace MapTo
|
||||||
".Trim();
|
".Trim();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
@ -407,7 +406,7 @@ namespace MapTo
|
||||||
".Trim();
|
".Trim();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
@ -441,7 +440,7 @@ namespace MapTo
|
||||||
".Trim();
|
".Trim();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
|
Loading…
Reference in New Issue