Skip to content

Commit

Permalink
feat: support fields
Browse files Browse the repository at this point in the history
  • Loading branch information
latonz committed Apr 17, 2023
1 parent e65b4e4 commit d2fe807
Show file tree
Hide file tree
Showing 30 changed files with 299 additions and 86 deletions.
1 change: 1 addition & 0 deletions docs/docs/02-configuration/01-mapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ except for ignored members)
set the following two EditorConfig settings (see also [analyzer diagnostics](./13-analyzer-diagnostics.mdx)):

```editorconfig title=".editorconfig"
[*.cs]
dotnet_diagnostic.RMG012.severity = error # Unmapped target member
dotnet_diagnostic.RMG020.severity = error # Unmapped source member
```
1 change: 1 addition & 0 deletions docs/docs/02-configuration/13-analyzer-diagnostics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Improvements are tracked in [#72](https://github.com/riok/mapperly/issues/72).
The severity of these diagnostics can be customized via an `.editorconfig` file:

```editorconfig title=".editorconfig"
[*.cs]
dotnet_diagnostic.{RuleID}.severity = error
dotnet_diagnostic.RMG020.severity = error
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.CodeAnalysis;
using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.MappingBodyBuilders.BuilderContext;

Expand All @@ -19,7 +19,7 @@ public interface IMembersBuilderContext<out T>

IReadOnlyCollection<string> IgnoredSourceMemberNames { get; }

Dictionary<string, IPropertySymbol> TargetMembers { get; }
Dictionary<string, IMappableMember> TargetMembers { get; }

Dictionary<string, List<MapPropertyAttribute>> MemberConfigsByRootTargetName { get; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Riok.Mapperly.Descriptors.Mappings.MemberMappings;
using Riok.Mapperly.Diagnostics;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.MappingBodyBuilders.BuilderContext;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using Microsoft.CodeAnalysis;
using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Descriptors.Mappings.MemberMappings;
using Riok.Mapperly.Diagnostics;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.MappingBodyBuilders.BuilderContext;

Expand Down Expand Up @@ -44,7 +43,7 @@ protected MembersMappingBuilderContext(MappingBuilderContext builderContext, T m

public IReadOnlyCollection<string> IgnoredSourceMemberNames { get; }

public Dictionary<string, IPropertySymbol> TargetMembers { get; }
public Dictionary<string, IMappableMember> TargetMembers { get; }

public Dictionary<string, List<MapPropertyAttribute>> MemberConfigsByRootTargetName { get; }

Expand Down Expand Up @@ -89,15 +88,15 @@ private HashSet<string> GetIgnoredSourceMembers()
private HashSet<string> GetSourceMemberNames()
{
return Mapping.SourceType
.GetAllAccessibleProperties()
.GetAccessibleMappableMembers()
.Select(x => x.Name)
.ToHashSet();
}

private Dictionary<string, IPropertySymbol> GetTargetMembers()
private Dictionary<string, IMappableMember> GetTargetMembers()
{
return Mapping.TargetType
.GetAllAccessibleProperties()
.GetAccessibleMappableMembers()
.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Riok.Mapperly.Descriptors.Mappings.MemberMappings;
using Riok.Mapperly.Diagnostics;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.MappingBodyBuilders;

Expand All @@ -30,9 +31,9 @@ public static void BuildMappingBody(MappingBuilderContext ctx, NewInstanceObject
ObjectMemberMappingBodyBuilder.BuildMappingBody(mappingCtx);
}

private static void BuildInitOnlyMemberMappings(INewInstanceBuilderContext<IMapping> ctx, bool includeAllProperties = false)
private static void BuildInitOnlyMemberMappings(INewInstanceBuilderContext<IMapping> ctx, bool includeAllMembers = false)
{
var initOnlyTargetMembers = includeAllProperties
var initOnlyTargetMembers = includeAllMembers
? ctx.TargetMembers.Values.ToArray()
: ctx.TargetMembers.Values.Where(x => x.CanOnlySetViaInitializer()).ToArray();
foreach (var targetMember in initOnlyTargetMembers)
Expand All @@ -52,7 +53,7 @@ private static void BuildInitOnlyMemberMappings(INewInstanceBuilderContext<IMapp
out var sourceMemberPath))
{
ctx.BuilderContext.ReportDiagnostic(
targetMember.IsRequired()
targetMember.IsRequired
? DiagnosticDescriptors.RequiredMemberNotMapped
: DiagnosticDescriptors.SourceMemberNotFound,
targetMember.Name,
Expand All @@ -67,7 +68,7 @@ private static void BuildInitOnlyMemberMappings(INewInstanceBuilderContext<IMapp

private static void BuildInitMemberMapping(
INewInstanceBuilderContext<IMapping> ctx,
IPropertySymbol targetMember,
IMappableMember targetMember,
IReadOnlyCollection<MapPropertyAttribute> memberConfigs)
{
// add configured mapping
Expand Down Expand Up @@ -108,13 +109,10 @@ private static void BuildInitMemberMapping(

private static void BuildInitMemberMapping(
INewInstanceBuilderContext<IMapping> ctx,
IPropertySymbol targetMember,
IMappableMember targetMember,
MemberPath sourcePath)
{
var targetPath = new MemberPath(new[]
{
targetMember
});
var targetPath = new MemberPath(new[] { targetMember });
if (!ObjectMemberMappingBodyBuilder.ValidateMappingSpecification(ctx, sourcePath, targetPath, true))
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Riok.Mapperly.Descriptors.Mappings.MemberMappings;
using Riok.Mapperly.Diagnostics;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.MappingBodyBuilders;

Expand Down Expand Up @@ -93,7 +94,7 @@ public static bool ValidateMappingSpecification(
bool allowInitOnlyMember = false)
{
// the target member path is readonly or not accessible
if (!targetMemberPath.Member.CanSet())
if (!targetMemberPath.Member.CanSet)
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.CannotMapToReadOnlyMember,
Expand All @@ -107,7 +108,7 @@ public static bool ValidateMappingSpecification(
}

// a target member path part is write only or not accessible
if (targetMemberPath.ObjectPath.Any(p => !p.CanGet()))
if (targetMemberPath.ObjectPath.Any(p => !p.CanGet))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.CannotMapToWriteOnlyMemberPath,
Expand All @@ -122,7 +123,7 @@ public static bool ValidateMappingSpecification(

// a target member path part is init only
var noInitOnlyPath = allowInitOnlyMember ? targetMemberPath.ObjectPath : targetMemberPath.Path;
if (noInitOnlyPath.Any(p => p.IsInitOnly()))
if (noInitOnlyPath.Any(p => p.IsInitOnly))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.CannotMapToInitOnlyMemberPath,
Expand All @@ -136,7 +137,7 @@ public static bool ValidateMappingSpecification(
}

// a source member path is write only or not accessible
if (sourceMemberPath.Path.Any(p => !p.CanGet()))
if (sourceMemberPath.Path.Any(p => !p.CanGet))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.CannotMapFromWriteOnlyMember,
Expand Down Expand Up @@ -209,7 +210,7 @@ private static void BuildMemberAssignmentMapping(

// the source is nullable, or the mapping is a direct assignment and the target allows nulls
// access the source in a null save matter (via ?.) but no other special handling required.
if (delegateMapping.SourceType.IsNullable() || delegateMapping.IsSynthetic && targetMemberPath.Member.IsNullable())
if (delegateMapping.SourceType.IsNullable() || delegateMapping.IsSynthetic && targetMemberPath.Member.IsNullable)
{
var memberMapping = new MemberMapping(
delegateMapping,
Expand All @@ -235,9 +236,9 @@ private static bool TryAddExistingTargetMapping(
// if the member is readonly
// and the target and source path is readable,
// we try to create an existing target mapping
if (targetMemberPath.Member.CanSet()
|| !targetMemberPath.Path.All(op => op.CanGet())
|| !sourceMemberPath.Path.All(op => op.CanGet()))
if (targetMemberPath.Member.CanSet
|| !targetMemberPath.Path.All(op => op.CanGet)
|| !sourceMemberPath.Path.All(op => op.CanGet))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ public static class DictionaryMappingBuilder
// The constructed type should be Dictionary<,>
if (IsDictionaryType(ctx, ctx.Target))
{
var sourceHasCount = ctx.Source.GetAllMembers(CountPropertyName)
.OfType<IPropertySymbol>()
var sourceHasCount = ctx.Source.GetAllProperties(CountPropertyName)
.Any(x => !x.IsStatic && !x.IsIndexer && !x.IsWriteOnly && x.Type.SpecialType == SpecialType.System_Int32);

var targetDictionarySymbol = ctx.Types.DictionaryT.Construct(keyMapping.TargetType, valueMapping.TargetType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ public static class ParseMappingBuilder

var targetIsNullable = ctx.Target.NonNullable(out var nonNullableTarget);

var parseMethodCandidates = nonNullableTarget.GetAllMembers(ParseMethodName)
.OfType<IMethodSymbol>()
var parseMethodCandidates = nonNullableTarget.GetAllMethods(ParseMethodName)
.Where(m =>
m.IsStatic
&& !m.ReturnsVoid
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.Mappings.MemberMappings;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.Mappings.MemberMappings;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Symbols;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Riok.Mapperly.Emit.SyntaxFactoryHelper;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Descriptors.Mappings.ExistingTarget;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.Mappings.MemberMappings;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.Mappings.MemberMappings;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Symbols;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Riok.Mapperly.Emit.SyntaxFactoryHelper;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Symbols;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Riok.Mapperly.Emit.SyntaxFactoryHelper;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Riok.Mapperly.Emit.SyntaxFactoryHelper;

Expand Down
4 changes: 2 additions & 2 deletions src/Riok.Mapperly/Descriptors/UserMethodMappingExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ private static IEnumerable<IMethodSymbol> ExtractMethods(ITypeSymbol mapperSymbo

private static IEnumerable<IMethodSymbol> ExtractBaseMethods(INamedTypeSymbol objectType, ITypeSymbol mapperSymbol)
{
var baseMethods = mapperSymbol.BaseType?.GetAllMembers() ?? Enumerable.Empty<ISymbol>();
var intfMethods = mapperSymbol.AllInterfaces.SelectMany(x => x.GetAllMembers());
var baseMethods = mapperSymbol.BaseType?.GetAllMethods() ?? Enumerable.Empty<ISymbol>();
var intfMethods = mapperSymbol.AllInterfaces.SelectMany(x => x.GetAllMethods());
return baseMethods
.Concat(intfMethods)
.OfType<IMethodSymbol>()
Expand Down
25 changes: 0 additions & 25 deletions src/Riok.Mapperly/Helpers/PropertySymbolExtensions.cs

This file was deleted.

31 changes: 24 additions & 7 deletions src/Riok.Mapperly/Helpers/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Helpers;

Expand Down Expand Up @@ -34,11 +35,14 @@ internal static bool TryGetEnumUnderlyingType(this ITypeSymbol t, [NotNullWhen(t
return enumType != null;
}

internal static IEnumerable<ISymbol> GetAllMembers(this ITypeSymbol symbol, string name)
=> symbol.GetAllMembers(name, StringComparer.Ordinal);
internal static IEnumerable<IMethodSymbol> GetAllMethods(this ITypeSymbol symbol)
=> symbol.GetAllMembers().OfType<IMethodSymbol>();

internal static IEnumerable<ISymbol> GetAllMembers(this ITypeSymbol symbol, string name, IEqualityComparer<string> comparer)
=> symbol.GetAllMembers().Where(x => comparer.Equals(name, x.Name));
internal static IEnumerable<IMethodSymbol> GetAllMethods(this ITypeSymbol symbol, string name)
=> symbol.GetAllMembers(name).OfType<IMethodSymbol>();

internal static IEnumerable<IPropertySymbol> GetAllProperties(this ITypeSymbol symbol, string name)
=> symbol.GetAllMembers(name).OfType<IPropertySymbol>();

internal static IEnumerable<ISymbol> GetAllMembers(this ITypeSymbol symbol)
{
Expand All @@ -55,13 +59,23 @@ internal static IEnumerable<ISymbol> GetAllMembers(this ITypeSymbol symbol)
: members.Concat(symbol.BaseType.GetAllMembers());
}

internal static IEnumerable<IPropertySymbol> GetAllAccessibleProperties(this ITypeSymbol symbol)
internal static IEnumerable<IMappableMember> GetMappableMembers(this ITypeSymbol symbol, string name, IEqualityComparer<string> comparer)
{
return symbol
.GetAllMembers()
.Where(x => comparer.Equals(name, x.Name) && !x.IsStatic)
.Select(MappableMember.Create)
.WhereNotNull();
}

internal static IEnumerable<IMappableMember> GetAccessibleMappableMembers(this ITypeSymbol symbol)
{
return symbol
.GetAllMembers()
.OfType<IPropertySymbol>()
.Where(x => x.IsAccessible())
.DistinctBy(x => x.Name);
.DistinctBy(x => x.Name)
.Select(MappableMember.Create)
.WhereNotNull();
}

internal static bool ImplementsGeneric(
Expand Down Expand Up @@ -125,4 +139,7 @@ internal static bool CanConsumeType(this ITypeParameterSymbol typeParameter, Com

return true;
}

private static IEnumerable<ISymbol> GetAllMembers(this ITypeSymbol symbol, string name)
=> symbol.GetAllMembers().Where(x => name.Equals(x.Name));
}
Loading

0 comments on commit d2fe807

Please sign in to comment.