Skip to content

Commit

Permalink
feat: add error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
TimothyMakkison committed Jul 17, 2023
1 parent fa8c2d3 commit 9da4123
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 79 deletions.
1 change: 1 addition & 0 deletions docs/docs/configuration/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Mapperly implements several types of automatic conversions (in order of priority
| Dictionary | Maps a source dictionary to an enumerable target | Source type is an `IDictionary<,>` or an `IReadOnlyDictionary<,>` |
| Enumerable | Maps an enumerable source to an enumerable target | Source type is an `IEnumerable<>` |
| Span | Maps a `Span<>`, `ReadOnlySpan<>` to or from `Span<>`, `ReadOnlySpan<>` or enumerable | Source or target type is a `Span<>`, `ReadOnlySpan<>` |
| Tuple | Create a new instance of a target `ValueTuple` or tuple expression `(10, 12)` | Target type is a `ValueTuple<>` or tuple expression |
| Memory | Maps a `Memory<>`, `ReadOnlyMemory<>` to or from `Memory<>`, `ReadOnlyMemory<>`, `Span<>`, `ReadOnlySpan<>` or enumerable | Source or target type is a `Memory<>` or `ReadOnlyMemory<>` |
| Implicit cast | Implicit cast operator | An implicit cast operator is defined to cast from the source type to the target type |
| Parse method | Uses a static `Parse` method on the target type | Source type is a `string` and target has a static method with the following signature: `TTarget Parse(string)`. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ out HashSet<string> mappedTargetMemberNames

foreach (var targetMember in namedTargetType.TupleElements)
{
if (!ctx.TargetMembers.ContainsKey(targetMember.Name))
{
return false;
}
if (!TryFindConstructorParameterSourcePath(ctx, targetMember, out var sourcePath))
{
ctx.BuilderContext.ReportDiagnostic(
Expand Down Expand Up @@ -154,7 +158,7 @@ private static bool TryFindConstructorParameterSourcePath(
return TryBuildConstructorParameterSourcePath(ctx, field, out sourcePath);
}

if (!MemberPath.TryFind(ctx.Mapping.SourceType, memberConfig.Source.Path, ctx.BuilderContext.SymbolAccessor, out sourcePath))
if (!ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(ctx.Mapping.SourceType, memberConfig.Source.Path, out sourcePath))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.SourceMemberNotFound,
Expand All @@ -180,12 +184,11 @@ out MemberPath? sourcePath
: StringComparer.OrdinalIgnoreCase;

if (
MemberPath.TryFind(
ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(
ctx.Mapping.SourceType,
MemberPathCandidateBuilder.BuildMemberPathCandidates(field.Name),
ctx.IgnoredSourceMemberNames,
memberNameComparer,
ctx.BuilderContext.SymbolAccessor,
out sourcePath
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ public static class NewInstanceObjectPropertyMappingBuilder
if (ctx.Source.IsEnum() || ctx.Target.IsEnum())
return null;

if (ctx.IsConversionEnabled(MappingConversionType.Enumerable) && ctx.Target.IsTupleType)
if (ctx.IsConversionEnabled(MappingConversionType.Tuple) && ctx.Target.IsTupleType)
{
// inline expressions don't support tuple expressions so ValueTuple is used instead
return ctx.IsExpression
? new NewValueTupleConstructorMapping(ctx.Source, ctx.Target, ctx.Types.Get<ValueTuple>())
: new NewValueTupleExpressionMapping(ctx.Source, ctx.Target);
if (ctx.IsExpression)
{
return new NewValueTupleConstructorMapping(ctx.Source, ctx.Target);
}

var expectedArgumentCount = (ctx.Target as INamedTypeSymbol)!.TupleElements.Length;
return new NewValueTupleExpressionMapping(ctx.Source, ctx.Target, expectedArgumentCount);
}

// inline expressions don't support method property mappings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Descriptors.Mappings.MemberMappings;
using Riok.Mapperly.Emit;
using static Riok.Mapperly.Emit.SyntaxFactoryHelper;

namespace Riok.Mapperly.Descriptors.Mappings;

Expand All @@ -13,26 +13,21 @@ namespace Riok.Mapperly.Descriptors.Mappings;
/// </summary>
public class NewValueTupleConstructorMapping : TypeMapping, INewValueTupleMapping
{
private const string ValueTupleName = "global::System.ValueTuple";
private readonly HashSet<ValueTupleConstructorParameterMapping> _constructorPropertyMappings = new();
private readonly ITypeSymbol _valueTupleSymbol;

public NewValueTupleConstructorMapping(ITypeSymbol sourceType, ITypeSymbol targetType, ITypeSymbol valueTupleSymbol)
: base(sourceType, targetType)
{
_valueTupleSymbol = valueTupleSymbol;
}
public NewValueTupleConstructorMapping(ITypeSymbol sourceType, ITypeSymbol targetType)
: base(sourceType, targetType) { }

public void AddConstructorParameterMapping(ValueTupleConstructorParameterMapping mapping) => _constructorPropertyMappings.Add(mapping);

public override ExpressionSyntax Build(TypeMappingBuildContext ctx)
{
// new ValueTuple<T..>(ctorArgs)
var ctorArgs = _constructorPropertyMappings.Select(x => x.BuildArgument(ctx, insideExpression: true)).ToArray();
var genericName = SyntaxFactory.GenericName(SyntaxFactoryHelper.FullyQualifiedIdentifierName(_valueTupleSymbol));
var typeArguments = SyntaxFactoryHelper.TypeArgumentList(
(TargetType as INamedTypeSymbol)!.TypeArguments.Select(SyntaxFactoryHelper.NonNullableIdentifier).ToArray()
);
var genericName = SyntaxFactory.GenericName(ValueTupleName);
var typeArguments = TypeArgumentList((TargetType as INamedTypeSymbol)!.TypeArguments.Select(NonNullableIdentifier));
var typedValue = genericName.WithTypeArgumentList(typeArguments);
return SyntaxFactory.ObjectCreationExpression(typedValue).WithArgumentList(SyntaxFactoryHelper.ArgumentList(ctorArgs));
return SyntaxFactory.ObjectCreationExpression(typedValue).WithArgumentList(ArgumentList(ctorArgs));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ namespace Riok.Mapperly.Descriptors.Mappings;
/// </summary>
public class NewValueTupleExpressionMapping : ObjectMemberMethodMapping, INewValueTupleMapping
{
private readonly int _argumentCount;
private const string NoMappingComment = "// Could not generate mapping";
private const string TargetVariableName = "target";
private readonly HashSet<ValueTupleConstructorParameterMapping> _constructorPropertyMappings = new();

public NewValueTupleExpressionMapping(ITypeSymbol sourceType, ITypeSymbol targetType)
: base(sourceType, targetType) { }
public NewValueTupleExpressionMapping(ITypeSymbol sourceType, ITypeSymbol targetType, int argumentCount)
: base(sourceType, targetType)
{
_argumentCount = argumentCount;
}

public void AddConstructorParameterMapping(ValueTupleConstructorParameterMapping mapping) => _constructorPropertyMappings.Add(mapping);

Expand All @@ -29,6 +34,11 @@ public NewValueTupleExpressionMapping(ITypeSymbol sourceType, ITypeSymbol target

public override ExpressionSyntax Build(TypeMappingBuildContext ctx)
{
if (_constructorPropertyMappings.Count != _argumentCount)
{
return ThrowNotImplementedException().WithLeadingTrivia(TriviaList(Comment(NoMappingComment)));
}

if (HasMemberMappings())
{
return base.Build(ctx);
Expand All @@ -40,6 +50,12 @@ public override ExpressionSyntax Build(TypeMappingBuildContext ctx)

public override IEnumerable<StatementSyntax> BuildBody(TypeMappingBuildContext ctx)
{
if (_constructorPropertyMappings.Count != _argumentCount)
{
yield return ExpressionStatement(ThrowNotImplementedException()).WithLeadingTrivia(TriviaList(Comment(NoMappingComment)));
yield break;
}

// (Name:.. ,..);
var ctorArgs = _constructorPropertyMappings.Select(x => x.BuildArgument(ctx, insideExpression: false)).ToArray();
var tupleCreationExpression = TupleExpression(SeparatedList(ctorArgs));
Expand Down
3 changes: 3 additions & 0 deletions src/Riok.Mapperly/Emit/SyntaxFactoryHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,9 @@ public static ArgumentListSyntax ArgumentList(params ExpressionSyntax[] argSynta
public static TypeArgumentListSyntax TypeArgumentList(params TypeSyntax[] argSyntaxes) =>
SyntaxFactory.TypeArgumentList(CommaSeparatedList(argSyntaxes));

public static TypeArgumentListSyntax TypeArgumentList(IEnumerable<TypeSyntax> argSyntaxes) =>
SyntaxFactory.TypeArgumentList(CommaSeparatedList(argSyntaxes));

public static ArgumentListSyntax ArgumentList(params ArgumentSyntax[] args) => SyntaxFactory.ArgumentList(CommaSeparatedList(args));

public static SeparatedSyntaxList<T> CommaSeparatedList<T>(IEnumerable<T> nodes, bool insertTrailingComma = false)
Expand Down
Loading

0 comments on commit 9da4123

Please sign in to comment.