Fix incorrect usings generator + cleanup.
This commit is contained in:
parent
4734b8170a
commit
3ff1e90fb9
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace MapTo
|
||||
{
|
||||
internal static class Diagnostics
|
||||
{
|
||||
private const string UsageCategory = "Usage";
|
||||
|
||||
internal static Diagnostic SymbolNotFound(Location location, string syntaxName) =>
|
||||
Diagnostic.Create(CreateDescriptor("MT0001", "Symbol not found.", $"Unable to find any symbols for {syntaxName}"), location);
|
||||
|
||||
internal static Diagnostic MapFromAttributeNotFound(Location location) =>
|
||||
Diagnostic.Create(CreateDescriptor("MT0002", "Attribute Not Available", $"Unable to find {SourceBuilder.MapFromAttributeName} type."), location);
|
||||
|
||||
internal static Diagnostic ClassMappingsGenerated(Location location, string typeName) =>
|
||||
Diagnostic.Create(CreateDescriptor("MT1001", "Mapped Type", $"Generated mappings for {typeName}", DiagnosticSeverity.Info), location);
|
||||
|
||||
private static DiagnosticDescriptor CreateDescriptor(string id, string title, string message, DiagnosticSeverity severity = DiagnosticSeverity.Error) =>
|
||||
new(id, title, message, UsageCategory, severity, true);
|
||||
}
|
||||
}
|
|
@ -28,9 +28,9 @@ namespace MapTo.Extensions
|
|||
return type.GetAllMembers().OfType<T>();
|
||||
}
|
||||
|
||||
public static CompilationUnitSyntax? GetCompilationUnit(this SyntaxNode syntaxNode)
|
||||
public static CompilationUnitSyntax GetCompilationUnit(this SyntaxNode syntaxNode)
|
||||
{
|
||||
return syntaxNode.Ancestors().OfType<CompilationUnitSyntax>().FirstOrDefault();
|
||||
return syntaxNode.Ancestors().OfType<CompilationUnitSyntax>().Single();
|
||||
}
|
||||
|
||||
public static string GetClassName(this ClassDeclarationSyntax classSyntax)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MapTo.Extensions;
|
||||
using MapTo.Models;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
@ -25,61 +26,50 @@ namespace MapTo
|
|||
return;
|
||||
}
|
||||
|
||||
foreach (var (classDeclarationSyntax, attributeSyntax) in receiver.CandidateClasses)
|
||||
foreach (var classDeclarationSyntax in receiver.CandidateClasses)
|
||||
{
|
||||
var model = GetModel(context.Compilation, classDeclarationSyntax);
|
||||
var (model, diagnostic) = GetModel(context.Compilation, classDeclarationSyntax);
|
||||
if (model is null)
|
||||
{
|
||||
// TODO: Emit diagnostic info.
|
||||
context.ReportDiagnostic(diagnostic!);
|
||||
continue;
|
||||
}
|
||||
|
||||
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
||||
|
||||
context.AddSource(hintName, source);
|
||||
context.ReportDiagnostic(Diagnostics.ClassMappingsGenerated(classDeclarationSyntax.GetLocation(), model.ClassName));
|
||||
}
|
||||
}
|
||||
|
||||
private static MapModel? GetModel(Compilation compilation, ClassDeclarationSyntax classSyntax)
|
||||
private static (MapModel? model, Diagnostic? diagnostic) GetModel(Compilation compilation, ClassDeclarationSyntax classSyntax)
|
||||
{
|
||||
var root = classSyntax.GetCompilationUnit();
|
||||
if (root is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var classSemanticModel = compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
||||
|
||||
if (!(classSemanticModel.GetDeclaredSymbol(classSyntax) is INamedTypeSymbol classSymbol))
|
||||
{
|
||||
return null;
|
||||
return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
||||
}
|
||||
|
||||
var destinationTypeSymbol = GetDestinationTypeSymbol(classSyntax, classSemanticModel);
|
||||
if (destinationTypeSymbol is null)
|
||||
var sourceTypeSymbol = GetSourceTypeSymbol(classSyntax, classSemanticModel);
|
||||
if (sourceTypeSymbol is null)
|
||||
{
|
||||
return null;
|
||||
return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
||||
}
|
||||
|
||||
return new MapModel(
|
||||
ns: root.GetNamespace(),
|
||||
classModifiers: classSyntax.Modifiers,
|
||||
className: classSyntax.GetClassName(),
|
||||
properties: classSymbol.GetAllMembersOfType<IPropertySymbol>(),
|
||||
sourceNamespace: destinationTypeSymbol.ContainingNamespace.Name,
|
||||
sourceClassName: destinationTypeSymbol.Name,
|
||||
sourceTypeProperties: destinationTypeSymbol.GetAllMembersOfType<IPropertySymbol>());
|
||||
|
||||
return (MapModel.Create(root, classSyntax, classSymbol, sourceTypeSymbol), default);
|
||||
}
|
||||
|
||||
private static ITypeSymbol? GetDestinationTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model)
|
||||
private static ITypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model)
|
||||
{
|
||||
var destinationTypeExpressionSyntax = classSyntax
|
||||
var sourceTypeExpressionSyntax = classSyntax
|
||||
.GetAttribute(SourceBuilder.MapFromAttributeName)
|
||||
?.DescendantNodes()
|
||||
.OfType<TypeOfExpressionSyntax>()
|
||||
.SingleOrDefault();
|
||||
|
||||
return destinationTypeExpressionSyntax is not null ? model.GetTypeInfo(destinationTypeExpressionSyntax.Type).Type : null;
|
||||
return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ namespace MapTo
|
|||
{
|
||||
internal class MapToSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
public List<(ClassDeclarationSyntax classDeclarationSyntax, AttributeSyntax attributeSyntax)> CandidateClasses { get; } = new();
|
||||
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
|
@ -31,7 +31,7 @@ namespace MapTo
|
|||
|
||||
if (attributeSyntax is not null)
|
||||
{
|
||||
CandidateClasses.Add((classDeclaration, attributeSyntax));
|
||||
CandidateClasses.Add(classDeclaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
using MapTo.Extensions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace MapTo.Models
|
||||
{
|
||||
public class MapModel
|
||||
{
|
||||
public MapModel(string? ns, SyntaxTokenList classModifiers, string className, IEnumerable<IPropertySymbol> properties, string sourceNamespace, string sourceClassName, IEnumerable<IPropertySymbol> sourceTypeProperties)
|
||||
private MapModel(
|
||||
string? ns,
|
||||
SyntaxTokenList classModifiers,
|
||||
string className,
|
||||
IEnumerable<IPropertySymbol> properties,
|
||||
string sourceNamespace,
|
||||
string sourceClassName,
|
||||
string sourceClassFullName,
|
||||
IEnumerable<IPropertySymbol> sourceTypeProperties)
|
||||
{
|
||||
Namespace = ns;
|
||||
ClassModifiers = classModifiers;
|
||||
|
@ -13,6 +23,7 @@ namespace MapTo.Models
|
|||
Properties = properties;
|
||||
SourceNamespace = sourceNamespace;
|
||||
SourceClassName = sourceClassName;
|
||||
SourceClassFullName = sourceClassFullName;
|
||||
SourceTypeProperties = sourceTypeProperties;
|
||||
}
|
||||
|
||||
|
@ -20,14 +31,29 @@ namespace MapTo.Models
|
|||
|
||||
public SyntaxTokenList ClassModifiers { get; }
|
||||
|
||||
public string ClassName { get; }
|
||||
|
||||
public string ClassName { get; }
|
||||
|
||||
public IEnumerable<IPropertySymbol> Properties { get; }
|
||||
|
||||
|
||||
public string SourceNamespace { get; }
|
||||
|
||||
|
||||
public string SourceClassName { get; }
|
||||
|
||||
|
||||
public string SourceClassFullName { get; }
|
||||
|
||||
public IEnumerable<IPropertySymbol> SourceTypeProperties { get; }
|
||||
|
||||
internal static MapModel Create(CompilationUnitSyntax root, ClassDeclarationSyntax classSyntax, INamedTypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
||||
{
|
||||
return new(
|
||||
root.GetNamespace(),
|
||||
classSyntax.Modifiers,
|
||||
classSyntax.GetClassName(),
|
||||
classSymbol.GetAllMembersOfType<IPropertySymbol>(),
|
||||
sourceTypeSymbol.ContainingNamespace.ToString(),
|
||||
sourceTypeSymbol.Name,
|
||||
sourceTypeSymbol.ToString(),
|
||||
sourceTypeSymbol.GetAllMembersOfType<IPropertySymbol>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ namespace MapTo
|
|||
.AppendLine()
|
||||
.AppendLine()
|
||||
.PadLeft(Indent1)
|
||||
.AppendFormat("{0} static class {1}Extensions", model.ClassModifiers.FirstOrDefault().ToFullString().Trim(), model.SourceClassName)
|
||||
.AppendFormat("{0} static partial class {1}Extensions", model.ClassModifiers.FirstOrDefault().ToFullString().Trim(), model.SourceClassName)
|
||||
.AppendOpeningBracket(Indent1)
|
||||
|
||||
// Extension class body
|
||||
|
@ -87,7 +87,8 @@ namespace MapTo
|
|||
{
|
||||
builder.AppendLine("using System;");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(model.SourceNamespace) && model.Namespace != model.SourceNamespace)
|
||||
// NB: If class names are the same, we're going to use fully qualified names instead.
|
||||
if (model.Namespace != model.SourceNamespace)
|
||||
{
|
||||
builder.AppendFormat("using {0};", model.SourceNamespace).AppendLine();
|
||||
}
|
||||
|
@ -101,7 +102,7 @@ namespace MapTo
|
|||
|
||||
builder
|
||||
.PadLeft(Indent2)
|
||||
.AppendFormat("public {0}({1} {2})", model.ClassName, model.SourceClassName, sourceClassParameterName)
|
||||
.AppendFormat("public {0}({1} {2})", model.ClassName, model.SourceClassFullName, sourceClassParameterName)
|
||||
.AppendOpeningBracket(Indent2)
|
||||
.PadLeft(Indent3)
|
||||
.AppendFormat("if ({0} == null) throw new ArgumentNullException(nameof({0}));", sourceClassParameterName)
|
||||
|
@ -130,7 +131,7 @@ namespace MapTo
|
|||
return builder
|
||||
.AppendLine()
|
||||
.PadLeft(Indent2)
|
||||
.AppendFormat("public static {0} From({1} {2})", model.ClassName, model.SourceClassName, sourceClassParameterName)
|
||||
.AppendFormat("public static {0} From({1} {2})", model.ClassName, model.SourceClassFullName, sourceClassParameterName)
|
||||
.AppendOpeningBracket(Indent2)
|
||||
.PadLeft(Indent3)
|
||||
.AppendFormat("return {0} == null ? null : new {1}({0});", sourceClassParameterName, model.ClassName)
|
||||
|
@ -143,10 +144,10 @@ namespace MapTo
|
|||
|
||||
return builder
|
||||
.PadLeft(Indent2)
|
||||
.AppendFormat("public static {0} To{0}(this {1} {2})", model.ClassName, model.SourceClassName, sourceClassParameterName)
|
||||
.AppendFormat("public static {0} To{0}(this {1} {2})", model.ClassName, model.SourceClassFullName, sourceClassParameterName)
|
||||
.AppendOpeningBracket(Indent2)
|
||||
.PadLeft(Indent3)
|
||||
.AppendFormat("return {0} == null ? null : new {1}({0})", sourceClassParameterName, model.ClassName)
|
||||
.AppendFormat("return {0} == null ? null : new {1}({0});", sourceClassParameterName, model.ClassName)
|
||||
.AppendClosingBracket(Indent2);
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace Test
|
|||
{
|
||||
public partial class Foo
|
||||
{
|
||||
public Foo(Baz baz)
|
||||
public Foo(Test.Baz baz)
|
||||
{
|
||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ namespace Test
|
|||
{
|
||||
public partial class Foo
|
||||
{
|
||||
public Foo(Baz baz)
|
||||
public Foo(Test.Baz baz)
|
||||
{
|
||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ namespace Test
|
|||
const string expectedResult = @"
|
||||
public partial class Foo
|
||||
{
|
||||
public Foo(Baz baz)
|
||||
public Foo(Test.Models.Baz baz)
|
||||
{
|
||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||
Prop1 = baz.Prop1;
|
||||
|
@ -185,7 +185,7 @@ namespace Test
|
|||
var source = GetSourceText();
|
||||
|
||||
const string expectedResult = @"
|
||||
public static Foo From(Baz baz)
|
||||
public static Foo From(Test.Models.Baz baz)
|
||||
{
|
||||
return baz == null ? null : new Foo(baz);
|
||||
}
|
||||
|
@ -230,9 +230,9 @@ using Bazaar;
|
|||
const string expectedResult = @"
|
||||
public static class BazExtensions
|
||||
{
|
||||
public static Foo ToFoo(this Baz baz)
|
||||
public static Foo ToFoo(this Test.Models.Baz baz)
|
||||
{
|
||||
return baz == null ? null : new Foo(baz)
|
||||
return baz == null ? null : new Foo(baz);
|
||||
}
|
||||
}
|
||||
";
|
||||
|
@ -246,7 +246,7 @@ using Bazaar;
|
|||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||
}
|
||||
|
||||
private static string GetSourceText(bool includeAttributeNamespace = false, string sourceClassNamespace = "Test")
|
||||
private static string GetSourceText(bool includeAttributeNamespace = false, string sourceClassNamespace = "Test.Models")
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine($@"
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace TestConsoleApp
|
|||
static void Main(string[] args)
|
||||
{
|
||||
var userViewModel = User.From(new Data.Models.User());
|
||||
var userViewModel2 = UserViewModel.From(new Data.Models.User());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using MapTo;
|
||||
|
||||
namespace TestConsoleApp.ViewModels
|
||||
{
|
||||
[MapFrom(typeof(Data.Models.User))]
|
||||
public partial class UserViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue