diff --git a/MapTo/Extensions/EnumerableExtensions.cs b/MapTo/Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000..21d0887 --- /dev/null +++ b/MapTo/Extensions/EnumerableExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace MapTo.Extensions +{ + internal static class EnumerableExtensions + { + internal static void ForEach(this IEnumerable enumerable, Action action) + { + foreach (var item in enumerable) + { + action(item); + } + } + } +} \ No newline at end of file diff --git a/MapTo/MapToGenerator.cs b/MapTo/MapToGenerator.cs index 8b70066..e3b28a6 100644 --- a/MapTo/MapToGenerator.cs +++ b/MapTo/MapToGenerator.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using MapTo.Extensions; using MapTo.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -28,19 +29,25 @@ namespace MapTo private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, IEnumerable candidateClasses) { - foreach (var classDeclarationSyntax in candidateClasses) + foreach (var classSyntax in candidateClasses) { - var (model, diagnostic) = MapModel.Create(context.Compilation, classDeclarationSyntax); - if (model is null) + var root = classSyntax.GetCompilationUnit(); + var classSemanticModel = context.Compilation.GetSemanticModel(classSyntax.SyntaxTree); + var classSymbol = classSemanticModel.GetDeclaredSymbol(classSyntax) as INamedTypeSymbol; + var sourceTypeSymbol = GetSourceTypeSymbol(classSyntax, classSemanticModel); + + var (isValid, diagnostics) = Verify(root, classSyntax, classSemanticModel, classSymbol, sourceTypeSymbol); + if (!isValid) { - context.ReportDiagnostic(diagnostic!); + diagnostics.ForEach(context.ReportDiagnostic); continue; } + + var model = new MapModel(root, classSyntax, classSymbol!, sourceTypeSymbol!); var (source, hintName) = SourceBuilder.GenerateSource(model); - context.AddSource(hintName, source); - context.ReportDiagnostic(Diagnostics.ClassMappingsGenerated(classDeclarationSyntax.GetLocation(), model.ClassName)); + context.ReportDiagnostic(Diagnostics.ClassMappingsGenerated(classSyntax.GetLocation(), model.ClassName)); } } @@ -49,5 +56,33 @@ namespace MapTo var (source, hintName) = SourceBuilder.GenerateMapFromAttribute(); context.AddSource(hintName, source); } + + private static INamedTypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model) + { + var sourceTypeExpressionSyntax = classSyntax + .GetAttribute(SourceBuilder.MapFromAttributeName) + ?.DescendantNodes() + .OfType() + .SingleOrDefault(); + + return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type as INamedTypeSymbol : null; + } + + private static (bool isValid, IEnumerable diagnostics) Verify(CompilationUnitSyntax root, ClassDeclarationSyntax classSyntax, SemanticModel classSemanticModel, INamedTypeSymbol? classSymbol, INamedTypeSymbol? sourceTypeSymbol) + { + var diagnostics = new List(); + + if (classSymbol is null) + { + diagnostics.Add(Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText)); + } + + if (sourceTypeSymbol is null) + { + diagnostics.Add(Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText)); + } + + return (!diagnostics.Any(), diagnostics); + } } } \ No newline at end of file diff --git a/MapTo/Models/MapModel.cs b/MapTo/Models/MapModel.cs index c51bcfb..05ca354 100644 --- a/MapTo/Models/MapModel.cs +++ b/MapTo/Models/MapModel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using MapTo.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -28,6 +27,17 @@ namespace MapTo.Models SourceTypeProperties = sourceTypeProperties; } + internal MapModel(CompilationUnitSyntax root, ClassDeclarationSyntax classSyntax, ITypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol) + : this( + root.GetNamespace(), + classSyntax.Modifiers, + classSyntax.GetClassName(), + classSymbol.GetAllMembersOfType(), + sourceTypeSymbol.ContainingNamespace.ToString(), + sourceTypeSymbol.Name, + sourceTypeSymbol.ToString(), + sourceTypeSymbol.GetAllMembersOfType()) { } + public string? Namespace { get; } public SyntaxTokenList ClassModifiers { get; } @@ -43,45 +53,5 @@ namespace MapTo.Models public string SourceClassFullName { get; } public IEnumerable SourceTypeProperties { get; } - - internal static (MapModel? model, Diagnostic? diagnostic) Create(Compilation compilation, ClassDeclarationSyntax classSyntax) - { - var root = classSyntax.GetCompilationUnit(); - var classSemanticModel = compilation.GetSemanticModel(classSyntax.SyntaxTree); - - if (!(classSemanticModel.GetDeclaredSymbol(classSyntax) is INamedTypeSymbol classSymbol)) - { - return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText)); - } - - var sourceTypeSymbol = GetSourceTypeSymbol(classSyntax, classSemanticModel); - if (sourceTypeSymbol is null) - { - return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText)); - } - - var model = new MapModel( - root.GetNamespace(), - classSyntax.Modifiers, - classSyntax.GetClassName(), - classSymbol.GetAllMembersOfType(), - sourceTypeSymbol.ContainingNamespace.ToString(), - sourceTypeSymbol.Name, - sourceTypeSymbol.ToString(), - sourceTypeSymbol.GetAllMembersOfType()); - - return (model, default); - } - - private static ITypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model) - { - var sourceTypeExpressionSyntax = classSyntax - .GetAttribute(SourceBuilder.MapFromAttributeName) - ?.DescendantNodes() - .OfType() - .SingleOrDefault(); - - return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type : null; - } } } \ No newline at end of file