private async Task <IReadOnlyList <SyntaxNode> > GenerateMappingCode(IMethodSymbol methodSymbol, SyntaxGenerator generator, SemanticModel semanticModel, MappingContext mappingContext) { var matchedImplementor = implementors.FirstOrDefault(x => x.CanImplement(methodSymbol)); if (matchedImplementor != null) { return(await matchedImplementor.GenerateImplementation(methodSymbol, generator, semanticModel, mappingContext).ConfigureAwait(false)); } return(Array.Empty <SyntaxNode>()); }
public async Task <MappingElement> MapExpression(MappingElement source, AnnotatedType targetType, MappingContext mappingContext, MappingPath mappingPath = null) { if (source == null) { return(null); } if (mappingPath == null) { mappingPath = new MappingPath(); } var sourceType = source.ExpressionType; if (mappingPath.AddToMapped(sourceType.Type) == false) { return(new MappingElement() { ExpressionType = sourceType, Expression = source.Expression.WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */")), }); } if (mappingContext.FindConversion(sourceType, targetType) is {} userDefinedConversion) { //TODO: Check if conversion accept nullable type var invocationExpression = (ExpressionSyntax)syntaxGenerator.InvocationExpression(userDefinedConversion.Conversion, source.Expression); var protectResultFromNull = targetType.CanBeNull == false && userDefinedConversion.ToType.CanBeNull; var conversionExpression = protectResultFromNull ? OrFailWhenExpressionNull(invocationExpression) : invocationExpression; return(new MappingElement { ExpressionType = targetType, Expression = HandleSafeNull(source, userDefinedConversion.FromType, conversionExpression) }); } if (ObjectHelper.IsSimpleType(targetType.Type) && SymbolHelper.IsNullable(sourceType.Type, out var underlyingType)) { var mapping = new MappingElement { Expression = OrFailWhenArgumentNull(source.Expression), ExpressionType = new AnnotatedType(underlyingType, false) }; return(IsConversionToSimpleTypeNeeded(targetType.Type, underlyingType) ? ConvertToSimpleType(targetType, mapping, mappingContext) : mapping); } if (IsConversionToSimpleTypeNeeded(targetType.Type, source.ExpressionType.Type)) { var conversion = ConvertToSimpleType(targetType, source, mappingContext); if (targetType.CanBeNull == false && conversion.ExpressionType.CanBeNull) { return(new MappingElement { ExpressionType = conversion.ExpressionType.AsNotNull(), Expression = OrFailWhenArgumentNull(conversion.Expression) }); } return(conversion); } if (ShouldCreateConversionBetweenTypes(targetType.Type, sourceType.Type)) { return(await TryToCreateMappingExpression(source, targetType, mappingPath, mappingContext).ConfigureAwait(false)); } if (source.ExpressionType.Type.Equals(targetType.Type) && source.ExpressionType.CanBeNull && targetType.CanBeNull == false) { return(new MappingElement { Expression = OrFailWhenArgumentNull(source.Expression), ExpressionType = source.ExpressionType.AsNotNull() }); } return(source); }
public async Task <ExpressionSyntax> CreateMappingLambdaAsync(string lambdaParameterName, AnnotatedType sourceListElementType, AnnotatedType targetListElementType, MappingPath mappingPath, MappingContext mappingContext) { var source = new MappingElement() { ExpressionType = sourceListElementType, Expression = syntaxGenerator.IdentifierName(lambdaParameterName) as ExpressionSyntax, }; var listElementMappingStm = await MapExpression(source, targetListElementType, mappingContext, mappingPath).ConfigureAwait(false); return((ExpressionSyntax)syntaxGenerator.ValueReturningLambdaExpression(lambdaParameterName, listElementMappingStm.Expression)); }
private static IEnumerable <IObjectField> GetUnwrappingProperties(ITypeSymbol wrapperType, ITypeSymbol wrappedType, MappingContext mappingContext) { return(wrapperType.GetObjectFields().Where(x => x.Type.Type.Equals(wrappedType) && x.CanBeGet(wrappedType, mappingContext))); }
private async Task <SyntaxNode> MapCollectionsAsync(MappingElement source, AnnotatedType targetListType, MappingPath mappingPath, MappingContext mappingContext) { var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(targetListType.Type); var sourceListElementType = MappingHelper.GetElementType(source.ExpressionType.Type); var targetListElementType = MappingHelper.GetElementType(targetListType.Type); if (sourceListElementType.CanBeNull && targetListElementType.CanBeNull == false) { var whereFilter = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, $"OfType<{sourceListElementType.Type.Name}>"); var lambdaParameterName = NameHelper.CreateLambdaParameterName(source.Expression); var mappingLambda = await CreateMappingLambdaAsync(lambdaParameterName, sourceListElementType.AsNotNull(), targetListElementType, mappingPath, mappingContext).ConfigureAwait(false); var selectExpression = SyntaxFactoryExtensions.CreateMethodAccessExpression(whereFilter, false, "Select", mappingLambda); var toList = AddMaterializeCollectionInvocation(syntaxGenerator, selectExpression, targetListType.Type, false); return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toList, isReadonlyCollection, syntaxGenerator)); } if (ShouldCreateConversionBetweenTypes(targetListElementType.Type, sourceListElementType.Type)) { var useConvert = CanUseConvert(source.ExpressionType.Type); var mapMethod = useConvert ? "ConvertAll": "Select"; var lambdaParameterName = NameHelper.CreateLambdaParameterName(source.Expression); var mappingLambda = await CreateMappingLambdaAsync(lambdaParameterName, sourceListElementType, targetListElementType, mappingPath, mappingContext).ConfigureAwait(false); var selectExpression = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, mapMethod, mappingLambda); var toList = useConvert? selectExpression: AddMaterializeCollectionInvocation(syntaxGenerator, selectExpression, targetListType.Type, false); return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toList, isReadonlyCollection, syntaxGenerator)); } var toListInvocation = AddMaterializeCollectionInvocation(syntaxGenerator, source.Expression, targetListType.Type, source.ExpressionType.CanBeNull); return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toListInvocation, isReadonlyCollection, syntaxGenerator)); }
private MappingElement TryToUnwrap(ITypeSymbol targetType, MappingElement element, MappingContext mappingContext) { var sourceAccess = element.Expression as SyntaxNode; var conversion = semanticModel.Compilation.ClassifyConversion(element.ExpressionType, targetType); if (conversion.Exists == false) { var wrapper = GetWrappingInfo(element.ExpressionType, targetType, mappingContext); if (wrapper.Type == WrapperInfoType.ObjectField) { return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.MemberAccessExpression(sourceAccess, wrapper.UnwrappingObjectField.Name), ExpressionType = wrapper.UnwrappingObjectField.Type }); } if (wrapper.Type == WrapperInfoType.Method) { var unwrappingMethodAccess = syntaxGenerator.MemberAccessExpression(sourceAccess, wrapper.UnwrappingMethod.Name); return(new MappingElement() { Expression = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(unwrappingMethodAccess), ExpressionType = wrapper.UnwrappingMethod.ReturnType }); } if (targetType.SpecialType == SpecialType.System_String && element.ExpressionType.TypeKind == TypeKind.Enum) { var toStringAccess = syntaxGenerator.MemberAccessExpression(element.Expression, "ToString"); return(new MappingElement() { Expression = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(toStringAccess), ExpressionType = targetType }); } if (element.ExpressionType.SpecialType == SpecialType.System_String && targetType.TypeKind == TypeKind.Enum) { var parseEnumAccess = syntaxGenerator.MemberAccessExpression(SyntaxFactory.ParseTypeName("System.Enum"), "Parse"); var enumType = SyntaxFactory.ParseTypeName(targetType.Name); var parseInvocation = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(parseEnumAccess, new[] { syntaxGenerator.TypeOfExpression(enumType), element.Expression, syntaxGenerator.TrueLiteralExpression() }); return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(enumType, parseInvocation), ExpressionType = targetType }); } } else if (conversion.IsExplicit) { return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(targetType, sourceAccess), ExpressionType = targetType }); } return(element); }
private static IEnumerable <IMethodSymbol> GetUnwrappingMethods(ITypeSymbol wrapperType, ITypeSymbol wrappedType, MappingContext mappingContext) { return(ObjectHelper.GetWithGetPrefixMethods(wrapperType).Where(x => x.ReturnType.Equals(wrappedType) && mappingContext.AccessibilityHelper.IsSymbolAccessible(x, wrappedType))); }
private MappingElement ConvertToSimpleType(AnnotatedType targetType, MappingElement source, MappingContext mappingContext) { var conversion = semanticModel.Compilation.ClassifyConversion(source.ExpressionType.Type, targetType.Type); if (conversion.Exists == false) { var wrapper = GetWrappingInfo(source.ExpressionType.Type, targetType.Type, mappingContext); if (wrapper.Type == WrapperInfoType.ObjectField) { return(new MappingElement { Expression = SyntaxFactoryExtensions.CreateMemberAccessExpression(source.Expression, source.ExpressionType.CanBeNull, wrapper.UnwrappingObjectField.Name), ExpressionType = wrapper.UnwrappingObjectField.Type }); } if (wrapper.Type == WrapperInfoType.Method) { return(new MappingElement { Expression = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, wrapper.UnwrappingMethod.Name), ExpressionType = new AnnotatedType(wrapper.UnwrappingMethod.ReturnType) }); } if (targetType.Type.SpecialType == SpecialType.System_String && source.ExpressionType.Type.TypeKind == TypeKind.Enum) { var toStringAccess = SyntaxFactoryExtensions.CreateMethodAccessExpression(source.Expression, source.ExpressionType.CanBeNull, "ToString"); return(new MappingElement { Expression = toStringAccess, ExpressionType = targetType }); } if (source.ExpressionType.Type.SpecialType == SpecialType.System_String && targetType.Type.TypeKind == TypeKind.Enum) { var parseEnumAccess = syntaxGenerator.MemberAccessExpression(SyntaxFactory.ParseTypeName("System.Enum"), "Parse"); var enumType = SyntaxFactory.ParseTypeName(targetType.Type.Name); var parseInvocation = (InvocationExpressionSyntax)syntaxGenerator.InvocationExpression(parseEnumAccess, new[] { syntaxGenerator.TypeOfExpression(enumType), source.Expression, syntaxGenerator.TrueLiteralExpression() }); return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(enumType, parseInvocation), ExpressionType = targetType.AsNotNull() }); } } else if (conversion.IsExplicit) { return(new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.CastExpression(targetType.Type, source.Expression), ExpressionType = targetType }); } return(source); }
public async Task <ExpressionSyntax> MapExpression(ExpressionSyntax sourceExpression, AnnotatedType sourceType, AnnotatedType destinationType, MappingContext mappingContext) { var mappingSource = new MappingElement { Expression = sourceExpression, ExpressionType = sourceType }; var mappingElement = await MapExpression(mappingSource, destinationType, mappingContext).ConfigureAwait(false); return(mappingElement.Expression); }
protected virtual async Task <MappingElement> TryToCreateMappingExpression(MappingElement source, AnnotatedType targetType, MappingPath mappingPath, MappingContext mappingContext) { //TODO: If source expression is method or constructor invocation then we should extract local variable and use it im mappings as a reference var namedTargetType = targetType.Type as INamedTypeSymbol; if (namedTargetType != null) { var directlyMappingConstructor = namedTargetType.Constructors.FirstOrDefault(c => c.Parameters.Length == 1 && c.Parameters[0].Type.Equals(source.ExpressionType.Type)); if (directlyMappingConstructor != null) { var creationExpression = CreateObject(targetType.Type, SyntaxFactory.ArgumentList().AddArguments(SyntaxFactory.Argument(source.Expression))); var shouldProtectAgainstNull = directlyMappingConstructor.Parameters[0].Type.CanBeNull() == false && source.ExpressionType.CanBeNull; return(new MappingElement { ExpressionType = targetType, Expression = shouldProtectAgainstNull ? HandleSafeNull(source, targetType, creationExpression) : creationExpression }); } } if (MappingHelper.IsMappingBetweenCollections(targetType.Type, source.ExpressionType.Type)) { var shouldProtectAgainstNull = source.ExpressionType.CanBeNull && targetType.CanBeNull == false; var collectionMapping = (await MapCollectionsAsync(source, targetType, mappingPath.Clone(), mappingContext).ConfigureAwait(false)) as ExpressionSyntax; return(new MappingElement { ExpressionType = targetType, Expression = shouldProtectAgainstNull ? OrFailWhenArgumentNull(collectionMapping, source.Expression.ToFullString()) : collectionMapping, }); } var subMappingSourceFinder = new ObjectMembersMappingSourceFinder(source.ExpressionType.AsNotNull(), source.Expression); if (namedTargetType != null) { //maybe there is constructor that accepts parameter matching source properties var constructorOverloadParameterSets = namedTargetType.Constructors.Select(x => x.Parameters); var matchedOverload = await MethodHelper.FindBestParametersMatch(subMappingSourceFinder, constructorOverloadParameterSets, mappingContext).ConfigureAwait(false); if (matchedOverload != null) { var argumentListSyntaxAsync = await matchedOverload.ToArgumentListSyntaxAsync(this, mappingContext).ConfigureAwait(false); var creationExpression = CreateObject(targetType.Type, argumentListSyntaxAsync); var matchedSources = matchedOverload.GetMatchedSources(); var restSourceFinder = new IgnorableMappingSourceFinder(subMappingSourceFinder, foundElement => { return(matchedSources.Any(x => x.Expression.IsEquivalentTo(foundElement.Expression))); }); var mappingMatcher = new SingleSourceMatcher(restSourceFinder); return(new MappingElement() { ExpressionType = new AnnotatedType(targetType.Type), Expression = await AddInitializerWithMappingAsync(creationExpression, mappingMatcher, targetType.Type, mappingContext, mappingPath).ConfigureAwait(false), }); } } var objectCreationExpressionSyntax = CreateObject(targetType.Type); var subMappingMatcher = new SingleSourceMatcher(subMappingSourceFinder); var objectCreationWithInitializer = await AddInitializerWithMappingAsync(objectCreationExpressionSyntax, subMappingMatcher, targetType.Type, mappingContext, mappingPath).ConfigureAwait(false); return(new MappingElement() { ExpressionType = new AnnotatedType(targetType.Type), Expression = HandleSafeNull(source, targetType, objectCreationWithInitializer) }); }
public async Task <IReadOnlyList <AssignmentExpressionSyntax> > MapUsingSimpleAssignment(IReadOnlyCollection <IObjectField> targets, IMappingMatcher mappingMatcher, MappingContext mappingContext, MappingPath mappingPath = null, SyntaxNode globalTargetAccessor = null) { if (mappingPath == null) { mappingPath = new MappingPath(); } var results = new List <AssignmentExpressionSyntax>(); foreach (var match in await mappingMatcher.MatchAll(targets, syntaxGenerator, mappingContext, globalTargetAccessor).ConfigureAwait(false)) { var sourceMappingElement = await MapExpression(match.Source, match.Target.ExpressionType, mappingContext, mappingPath.Clone()).ConfigureAwait(false); var sourceExpression = sourceMappingElement.Expression; if (sourceMappingElement.ExpressionType != match.Target.ExpressionType) { mappingContext.AddMissingConversion(sourceMappingElement.ExpressionType.Type, match.Target.ExpressionType.Type); if (mappingContext.WrapInCustomConversion) { var customConversionMethodName = syntaxGenerator.IdentifierName($"MapFrom{sourceMappingElement.ExpressionType.Type.Name}To{match.Target.ExpressionType.Type.Name}"); sourceExpression = (ExpressionSyntax)syntaxGenerator.InvocationExpression(customConversionMethodName, sourceExpression); } } var assignmentExpression = AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, match.Target.Expression, sourceExpression); results.Add(assignmentExpression); } return(results); }
public MappingElement MapExpression(MappingElement source, ITypeSymbol targetType, MappingContext mappingContext, MappingPath mappingPath = null) { if (source == null) { return(null); } if (mappingPath == null) { mappingPath = new MappingPath(); } var sourceType = source.ExpressionType; if (mappingPath.AddToMapped(sourceType) == false) { return(new MappingElement() { ExpressionType = sourceType, Expression = source.Expression.WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */")) }); } if (mappingContext.FindConversion(sourceType, targetType) is {} userDefinedConversion) { return(new MappingElement() { ExpressionType = targetType, Expression = (ExpressionSyntax)syntaxGenerator.InvocationExpression(userDefinedConversion, source.Expression) }); } if (ObjectHelper.IsSimpleType(targetType) && SymbolHelper.IsNullable(sourceType, out var underlyingType)) { source = new MappingElement() { Expression = (ExpressionSyntax)syntaxGenerator.MemberAccessExpression(source.Expression, "Value"), ExpressionType = underlyingType }; } if (IsUnwrappingNeeded(targetType, source)) { return(TryToUnwrap(targetType, source, mappingContext)); } if (ShouldCreateConversionBetweenTypes(targetType, sourceType)) { return(TryToCreateMappingExpression(source, targetType, mappingPath, mappingContext)); } return(source); }
public SyntaxNode CreateMappingLambda(string lambdaParameterName, ITypeSymbol sourceListElementType, ITypeSymbol targetListElementType, MappingPath mappingPath, MappingContext mappingContext) { var source = new MappingElement() { ExpressionType = sourceListElementType, Expression = syntaxGenerator.IdentifierName(lambdaParameterName) as ExpressionSyntax }; var listElementMappingStm = MapExpression(source, targetListElementType, mappingContext, mappingPath); return(syntaxGenerator.ValueReturningLambdaExpression(lambdaParameterName, listElementMappingStm.Expression)); }
private SyntaxNode MapCollections(SyntaxNode sourceAccess, ITypeSymbol sourceListType, ITypeSymbol targetListType, MappingPath mappingPath, MappingContext mappingContext) { var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(targetListType); var sourceListElementType = MappingHelper.GetElementType(sourceListType); var targetListElementType = MappingHelper.GetElementType(targetListType); if (ShouldCreateConversionBetweenTypes(targetListElementType, sourceListElementType)) { var useConvert = CanUseConvert(sourceListType); var selectAccess = useConvert ? syntaxGenerator.MemberAccessExpression(sourceAccess, "ConvertAll"): syntaxGenerator.MemberAccessExpression(sourceAccess, "Select"); var lambdaParameterName = NameHelper.CreateLambdaParameterName(sourceAccess); var mappingLambda = CreateMappingLambda(lambdaParameterName, sourceListElementType, targetListElementType, mappingPath, mappingContext); var selectInvocation = syntaxGenerator.InvocationExpression(selectAccess, mappingLambda); var toList = useConvert? selectInvocation: AddMaterializeCollectionInvocation(syntaxGenerator, selectInvocation, targetListType); return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toList, isReadonlyCollection, syntaxGenerator)); } var toListInvocation = AddMaterializeCollectionInvocation(syntaxGenerator, sourceAccess, targetListType); return(MappingHelper.WrapInReadonlyCollectionIfNecessary(toListInvocation, isReadonlyCollection, syntaxGenerator)); }
public async Task <BlockSyntax> GenerateMappingBlockAsync(IMethodSymbol methodSymbol, SyntaxGenerator generator, SemanticModel semanticModel, MappingContext mappingContext) { var mappingStatements = await GenerateMappingStatements(methodSymbol, generator, semanticModel, mappingContext).ConfigureAwait(false); return(SyntaxFactory.Block(mappingStatements).WithAdditionalAnnotations(Formatter.Annotation)); }
private static WrapperInfo GetWrappingInfo(ITypeSymbol wrapperType, ITypeSymbol wrappedType, MappingContext mappingContext) { var unwrappingProperties = GetUnwrappingProperties(wrapperType, wrappedType, mappingContext).ToList(); var unwrappingMethods = GetUnwrappingMethods(wrapperType, wrappedType, mappingContext).ToList(); if (unwrappingMethods.Count + unwrappingProperties.Count == 1) { if (unwrappingMethods.Count == 1) { return(new WrapperInfo(unwrappingMethods.First())); } return(new WrapperInfo(unwrappingProperties.First())); } return(new WrapperInfo()); }
public async Task <IReadOnlyList <StatementSyntax> > GenerateMappingStatements(IMethodSymbol methodSymbol, SyntaxGenerator generator, SemanticModel semanticModel, MappingContext mappingContext) { var mappingExpressions = await GenerateMappingCode(methodSymbol, generator, semanticModel, mappingContext).ConfigureAwait(false); var mappingStatements = mappingExpressions.Select(e => e.AsStatement()); return(mappingStatements.ToList()); }
protected virtual MappingElement TryToCreateMappingExpression(MappingElement source, ITypeSymbol targetType, MappingPath mappingPath, MappingContext mappingContext) { //TODO: If source expression is method or constructor invocation then we should extract local variable and use it im mappings as a reference var namedTargetType = targetType as INamedTypeSymbol; if (namedTargetType != null) { var directlyMappingConstructor = namedTargetType.Constructors.FirstOrDefault(c => c.Parameters.Length == 1 && c.Parameters[0].Type.Equals(source.ExpressionType)); if (directlyMappingConstructor != null) { var constructorParameters = SyntaxFactory.ArgumentList().AddArguments(SyntaxFactory.Argument(source.Expression)); var creationExpression = syntaxGenerator.ObjectCreationExpression(targetType, constructorParameters.Arguments); return(new MappingElement() { ExpressionType = targetType, Expression = (ExpressionSyntax)creationExpression }); } } if (MappingHelper.IsMappingBetweenCollections(targetType, source.ExpressionType)) { return(new MappingElement() { ExpressionType = targetType, Expression = MapCollections(source.Expression, source.ExpressionType, targetType, mappingPath.Clone(), mappingContext) as ExpressionSyntax }); } var subMappingSourceFinder = new ObjectMembersMappingSourceFinder(source.ExpressionType, source.Expression, syntaxGenerator); if (namedTargetType != null) { //maybe there is constructor that accepts parameter matching source properties var constructorOverloadParameterSets = namedTargetType.Constructors.Select(x => x.Parameters); var matchedOverload = MethodHelper.FindBestParametersMatch(subMappingSourceFinder, constructorOverloadParameterSets, mappingContext); if (matchedOverload != null) { var creationExpression = ((ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(targetType, matchedOverload.ToArgumentListSyntax(this, mappingContext).Arguments)); var matchedSources = matchedOverload.GetMatchedSources(); var restSourceFinder = new IgnorableMappingSourceFinder(subMappingSourceFinder, foundElement => { return(matchedSources.Any(x => x.Expression.IsEquivalentTo(foundElement.Expression))); }); var mappingMatcher = new SingleSourceMatcher(restSourceFinder); return(new MappingElement() { ExpressionType = targetType, Expression = AddInitializerWithMapping(creationExpression, mappingMatcher, targetType, mappingContext, mappingPath) }); } } var objectCreationExpressionSyntax = ((ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(targetType)); var subMappingMatcher = new SingleSourceMatcher(subMappingSourceFinder); return(new MappingElement() { ExpressionType = targetType, Expression = AddInitializerWithMapping(objectCreationExpressionSyntax, subMappingMatcher, targetType, mappingContext, mappingPath) }); }