private async Task <Document> GenerateSplatting(Document document, InvocationExpressionSyntax invocationExpression, CancellationToken cancellationToken) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken); var methodSymbol = semanticModel.GetSymbolInfo(invocationExpression, cancellationToken).CandidateSymbols.OfType <IMethodSymbol>().FirstOrDefault(); if (methodSymbol == null) { return(document); } var root = await document.GetSyntaxRootAsync(cancellationToken); var invalidArgument = invocationExpression.ArgumentList.Arguments.First(); var sourceType = semanticModel.GetTypeInfo(invalidArgument.Expression); var syntaxGenerator = SyntaxGenerator.GetGenerator(document); var mappingSourceFinder = new MappingSourceFinder(sourceType.Type, invalidArgument.Expression, syntaxGenerator, semanticModel); var argumentList = FindBestArgumentsMatch(methodSymbol, cancellationToken, mappingSourceFinder, semanticModel); if (argumentList == null) { return(document); } var newRoot = root.ReplaceNode(invocationExpression, invocationExpression.WithArgumentList(argumentList)); return(document.WithSyntaxRoot(newRoot)); }
public static ArgumentListSyntax FindBestArgumentsMatch(MappingSourceFinder mappingSourceFinder, IEnumerable <ImmutableArray <IParameterSymbol> > overloadParameterSets) { return(overloadParameterSets .Select(x => FindArgumentsMatch(x, mappingSourceFinder)) .Where(x => x.Arguments.Count > 0) .OrderByDescending(argumentList => argumentList.Arguments.Count) .FirstOrDefault()); }
public IEnumerable <SyntaxNode> MapTypes(ITypeSymbol sourceType, ITypeSymbol targetType, SyntaxNode globalSourceAccessor, SyntaxNode globbalTargetAccessor = null, bool targetExists = false, bool generatorContext = false) { if (IsMappingBetweenCollections(targetType, sourceType)) { var collectionMapping = MapCollections(globalSourceAccessor, sourceType, targetType); if (globbalTargetAccessor == null) { yield return(generator.ContextualReturnStatement(collectionMapping, generatorContext)); } else if (targetExists == false) { yield return(generator.CompleteAssignmentStatement(globbalTargetAccessor, collectionMapping)); } yield break; } var targetLocalVariableName = globbalTargetAccessor == null?ToLocalVariableName(targetType.Name) : ToLocalVariableName(globbalTargetAccessor.ToFullString()); var mappingSourceFinder = new MappingSourceFinder(sourceType, globalSourceAccessor, generator, semanticModel); if (targetExists == false) { var mappingConstructorParameters = FindMappingConstructorParameters(targetType, sourceType, mappingSourceFinder, (ExpressionSyntax)globalSourceAccessor); if (mappingConstructorParameters != null) { var init = generator.ObjectCreationExpression(targetType, mappingConstructorParameters.Arguments); if (globbalTargetAccessor == null) { yield return(generator.ContextualReturnStatement(init, generatorContext)); } else { yield return(generator.CompleteAssignmentStatement(globbalTargetAccessor, init)); } yield break; } else { var init = generator.ObjectCreationExpression(targetType); yield return(generator.LocalDeclarationStatement(targetLocalVariableName, init)); } } var localTargetIdentifier = targetExists? globbalTargetAccessor: generator.IdentifierName(targetLocalVariableName); foreach (var targetProperty in ObjectHelper.GetPublicPropertySymbols(targetType)) { if (targetProperty.SetMethod.DeclaredAccessibility != Accessibility.Public && globbalTargetAccessor.Kind() != SyntaxKind.ThisExpression) { continue; } var mappingSource = mappingSourceFinder.FindMappingSource(targetProperty.Name, targetProperty.Type); if (mappingSource == null) { continue; } if (IsMappingBetweenCollections(targetProperty.Type, mappingSource.ExpressionType)) { var targetAccess = generator.MemberAccessExpression(localTargetIdentifier, targetProperty.Name); var collectionMapping = MapCollections(mappingSource.Expression, mappingSource.ExpressionType, targetProperty.Type); yield return(generator.CompleteAssignmentStatement(targetAccess, collectionMapping)); } else if (ObjectHelper.IsSimpleType(targetProperty.Type) == false) { //TODO: What if both sides has the same type? //TODO: Reverse flattening var targetAccess = generator.MemberAccessExpression(localTargetIdentifier, targetProperty.Name); foreach (var complexPropertyMappingNode in MapTypes(mappingSource.ExpressionType, targetProperty.Type, mappingSource.Expression, targetAccess)) { yield return(complexPropertyMappingNode); } } else { var targetAccess = generator.MemberAccessExpression(localTargetIdentifier, targetProperty.Name); yield return(generator.CompleteAssignmentStatement(targetAccess, mappingSource.Expression)); } } if (globbalTargetAccessor == null) { yield return(generator.ContextualReturnStatement(localTargetIdentifier, generatorContext)); } else if (targetExists == false) { yield return(generator.CompleteAssignmentStatement(globbalTargetAccessor, localTargetIdentifier)); } }
private static ArgumentListSyntax FindMappingConstructorParameters(ITypeSymbol targetType, ITypeSymbol sourceType, MappingSourceFinder mappingSourceFinder, ExpressionSyntax globalSourceAccessor) { if (targetType is INamedTypeSymbol namedType) { var directlyMappingConstructor = namedType.Constructors.FirstOrDefault(c => c.Parameters.Length == 1 && c.Parameters[0].Type == sourceType); if (directlyMappingConstructor != null) { return(SyntaxFactory.ArgumentList().AddArguments(SyntaxFactory.Argument(globalSourceAccessor))); } var constructorOverloadParameterSets = namedType.Constructors.Select(x => x.Parameters); return(MethodHelper.FindBestArgumentsMatch(mappingSourceFinder, constructorOverloadParameterSets)); } return(null); }
private static ArgumentListSyntax FindBestArgumentsMatch(IMethodSymbol methodSymbol, CancellationToken cancellationToken, MappingSourceFinder mappingSourceFinder, SemanticModel semanticModel) { var overloadParameterSets = methodSymbol.DeclaringSyntaxReferences.Select(ds => { var overloadDeclaration = (MethodDeclarationSyntax)ds.GetSyntax(cancellationToken); var overloadMethod = semanticModel.GetDeclaredSymbol(overloadDeclaration); return(overloadMethod.Parameters); }); return(MethodHelper.FindBestArgumentsMatch(mappingSourceFinder, overloadParameterSets)); }
private static ArgumentListSyntax FindArgumentsMatch(ImmutableArray <IParameterSymbol> parameters, MappingSourceFinder mappingSourceFinder) { var argumentList = SyntaxFactory.ArgumentList(); foreach (var parameter in parameters) { var mappingSource = mappingSourceFinder.FindMappingSource(parameter.Name, parameter.Type); if (mappingSource != null) { var argument = SyntaxFactory.Argument(SyntaxFactory.NameColon(parameter.Name), SyntaxFactory.Token(SyntaxKind.None), mappingSource.Expression); argumentList = argumentList.AddArguments(argument); } } return(argumentList); }