private static IEnumerable <SyntaxNode> GenerateMappingCode(IMethodSymbol methodSymbol,
                                                                    SyntaxGenerator generator, MappingGenerator mappingGenerator, SemanticModel semanticModel)
        {
            if (SymbolHelper.IsPureMappingFunction(methodSymbol))
            {
                var source     = methodSymbol.Parameters[0];
                var targetType = methodSymbol.ReturnType;
                return(mappingGenerator.MapTypes(source.Type, targetType, generator.IdentifierName(source.Name)));
            }

            var isMappingConstructor = SymbolHelper.IsMappingConstructor(methodSymbol);

            if (SymbolHelper.IsUpdateThisObjectFunction(methodSymbol) || isMappingConstructor)
            {
                var source     = methodSymbol.Parameters[0];
                var targetType = methodSymbol.ContainingType;
                return(mappingGenerator.MapTypes(source.Type, targetType, generator.IdentifierName(source.Name), generator.ThisExpression(), targetExists: true, isConstructorContext: isMappingConstructor));
            }

            if (SymbolHelper.IsUpdateParameterFunction(methodSymbol))
            {
                var source = methodSymbol.Parameters[0];
                var target = methodSymbol.Parameters[1];
                return(mappingGenerator.MapTypes(source.Type, target.Type, generator.IdentifierName(source.Name), generator.IdentifierName(target.Name), targetExists: true));
            }

            var isMultiParameterConstructor = SymbolHelper.IsMultiParameterUpdateThisObjectFunction(methodSymbol);

            if (isMultiParameterConstructor || SymbolHelper.IsMultiParameterMappingConstructor(methodSymbol))
            {
                var sourceFinder = new LocalScopeMappingSourceFinder(semanticModel, methodSymbol.Parameters);
                return(ObjectHelper.GetPublicPropertySymbols(methodSymbol.ContainingType)
                       .Where(property => property.SetMethod != null || (property.CanBeSetOnlyFromConstructor() && isMultiParameterConstructor))
                       .Select(property => new
                {
                    source = sourceFinder.FindMappingSource(property.Name, property.Type),
                    target = new MappingElement()
                    {
                        Expression = SyntaxFactory.IdentifierName(property.Name),
                        ExpressionType = property.Type
                    }
                })
                       .Where(x => x.source != null)
                       .SelectMany(pair => mappingGenerator.Map(pair.source, pair.target)));
            }
            return(Enumerable.Empty <SyntaxNode>());
        }
Пример #2
0
        public MappingElement MapExpression(MappingElement element, ITypeSymbol targetType, MappingPath mappingPath = null)
        {
            if (element == null)
            {
                return(null);
            }

            if (mappingPath == null)
            {
                mappingPath = new MappingPath();
            }

            var sourceType = element.ExpressionType;

            if (mappingPath.AddToMapped(sourceType) == false)
            {
                return(new MappingElement()
                {
                    ExpressionType = sourceType,
                    Expression = element.Expression.WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */"))
                });
            }

            if (ObjectHelper.IsSimpleType(targetType) && SymbolHelper.IsNullable(sourceType, out var underlyingType))
            {
                element = new MappingElement()
                {
                    Expression     = (ExpressionSyntax)syntaxGenerator.MemberAccessExpression(element.Expression, "Value"),
                    ExpressionType = underlyingType
                };
            }

            if (IsUnwrappingNeeded(targetType, element))
            {
                return(TryToUnwrap(targetType, element));
            }


            if (ShouldCreateConversionBetweenTypes(targetType, sourceType))
            {
                return(TryToCreateMappingExpression(element, targetType, mappingPath));
            }

            return(element);
        }
Пример #3
0
        private async Task TryToRegisterRefactoring(CodeRefactoringContext context, SyntaxNode node)
        {
            switch (node)
            {
            case MethodDeclarationSyntax methodDeclaration:
                if (methodDeclaration.Parent.Kind() != SyntaxKind.InterfaceDeclaration && methodDeclaration.ParameterList.Parameters.Count > 0)
                {
                    var semanticModel = await context.Document.GetSemanticModelAsync();

                    var methodSymbol          = semanticModel.GetDeclaredSymbol(methodDeclaration);
                    var allParameterHaveNames = methodSymbol.Parameters.All(x => string.IsNullOrWhiteSpace(x.Name) == false);
                    if (allParameterHaveNames == false)
                    {
                        return;
                    }

                    if (SymbolHelper.IsPureMappingFunction(methodSymbol) ||
                        SymbolHelper.IsMultiParameterPureFunction(methodSymbol) ||
                        SymbolHelper.IsUpdateThisObjectFunction(methodSymbol) ||
                        SymbolHelper.IsUpdateParameterFunction(methodSymbol) ||
                        SymbolHelper.IsMultiParameterUpdateThisObjectFunction(methodSymbol)
                        )
                    {
                        context.RegisterRefactoring(CodeAction.Create(title: title, createChangedDocument: c => GenerateMappingMethodBody(context.Document, methodDeclaration, c), equivalenceKey: title));
                    }
                }
                break;

            case ConstructorDeclarationSyntax constructorDeclaration:
                if (constructorDeclaration.ParameterList.Parameters.Count >= 1)
                {
                    context.RegisterRefactoring(CodeAction.Create(title: title, createChangedDocument: c => GenerateMappingMethodBody(context.Document, constructorDeclaration, c), equivalenceKey: title));
                }
                break;

            case IdentifierNameSyntax _:
            case ParameterListSyntax _:
                await TryToRegisterRefactoring(context, node.Parent);

                break;
            }
        }
Пример #4
0
        public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
            var node = root.FindNode(context.Span);

            switch (node)
            {
                case MethodDeclarationSyntax methodDeclaration:
                    if (methodDeclaration.ParameterList.Parameters.Count > 0)
                    {
                        var semanticModel = await context.Document.GetSemanticModelAsync();
                        var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration);
                        var allParameterHaveNames = methodSymbol.Parameters.All(x => string.IsNullOrWhiteSpace(x.Name) == false);
                        if (allParameterHaveNames == false)
                        {
                            return;
                        }

                        if (SymbolHelper.IsPureMappingFunction(methodSymbol) ||
                            SymbolHelper.IsUpdateThisObjectFunction(methodSymbol) ||
                            SymbolHelper.IsUpdateParameterFunction(methodSymbol) ||
                            SymbolHelper.IsMultiParameterUpdateThisObjectFunction(methodSymbol)
                        )
                        {
                            context.RegisterRefactoring(CodeAction.Create(title: title, createChangedDocument: c => GenerateMappingMethodBody(context.Document, methodDeclaration, c), equivalenceKey: title));
                           
                        }
                    }
                    break;
                case ConstructorDeclarationSyntax constructorDeclaration:
                    if (constructorDeclaration.ParameterList.Parameters.Count >= 1)
                    {
                        context.RegisterRefactoring(CodeAction.Create(title: title, createChangedDocument: c => GenerateMappingMethodBody(context.Document, constructorDeclaration, c), equivalenceKey: title));
                    }
                    break;
            }
        }
Пример #5
0
        private void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            var methodNode = context.Node as MethodDeclarationSyntax;

            if (methodNode != null && methodNode.ParameterList.Parameters.Count > 0)
            {
                var methodSymbol          = context.SemanticModel.GetDeclaredSymbol(methodNode);
                var allParameterHaveNames = methodSymbol.Parameters.All(x => string.IsNullOrWhiteSpace(x.Name) == false);
                if (allParameterHaveNames == false)
                {
                    return;
                }

                if (SymbolHelper.IsPureMappingFunction(methodSymbol) ||
                    SymbolHelper.IsUpdateThisObjectFunction(methodSymbol) ||
                    SymbolHelper.IsUpdateParameterFunction(methodSymbol) ||
                    SymbolHelper.IsMultiParameterUpdateThisObjectFunction(methodSymbol)
                    )
                {
                    var diagnostic = Diagnostic.Create(MappingMethodRule, methodNode.Identifier.GetLocation());
                    context.ReportDiagnostic(diagnostic);
                }
            }
        }
        private static IEnumerable <SyntaxNode> GenerateMappingCode(IMethodSymbol methodSymbol, SyntaxGenerator generator, SemanticModel semanticModel)
        {
            if (SymbolHelper.IsIdentityMappingFunction(methodSymbol))
            {
                var cloneMappingEngine = new CloneMappingEngine(semanticModel, generator, methodSymbol.ContainingAssembly);
                var source             = methodSymbol.Parameters[0];
                var targetType         = methodSymbol.ReturnType;
                var newExpression      = cloneMappingEngine.MapExpression((ExpressionSyntax)generator.IdentifierName(source.Name), source.Type, targetType);
                return(new[] { generator.ReturnStatement(newExpression).WithAdditionalAnnotations(Formatter.Annotation) });
            }

            var mappingEngine = new MappingEngine(semanticModel, generator, methodSymbol.ContainingAssembly);

            if (SymbolHelper.IsPureMappingFunction(methodSymbol))
            {
                var source        = methodSymbol.Parameters[0];
                var targetType    = methodSymbol.ReturnType;
                var newExpression = mappingEngine.MapExpression((ExpressionSyntax)generator.IdentifierName(source.Name), source.Type, targetType);
                return(new[] { generator.ReturnStatement(newExpression).WithAdditionalAnnotations(Formatter.Annotation) });
            }

            //TODO: Pure mapping with multiple parameters

            if (SymbolHelper.IsUpdateParameterFunction(methodSymbol))
            {
                var source       = methodSymbol.Parameters[0];
                var target       = methodSymbol.Parameters[1];
                var targets      = ObjectHelper.GetFieldsThaCanBeSetPublicly(target.Type, methodSymbol.ContainingAssembly);
                var sourceFinder = new ObjectMembersMappingSourceFinder(source.Type, generator.IdentifierName(source.Name), generator);
                return(mappingEngine.MapUsingSimpleAssignment(generator, targets, sourceFinder, globalTargetAccessor: generator.IdentifierName(target.Name)));
            }

            if (SymbolHelper.IsUpdateThisObjectFunction(methodSymbol))
            {
                var source       = methodSymbol.Parameters[0];
                var sourceFinder = new ObjectMembersMappingSourceFinder(source.Type, generator.IdentifierName(source.Name), generator);
                var targets      = ObjectHelper.GetFieldsThaCanBeSetPrivately(methodSymbol.ContainingType);
                return(mappingEngine.MapUsingSimpleAssignment(generator, targets, sourceFinder));
            }

            if (SymbolHelper.IsMultiParameterUpdateThisObjectFunction(methodSymbol))
            {
                var sourceFinder = new LocalScopeMappingSourceFinder(semanticModel, methodSymbol.Parameters);
                var targets      = ObjectHelper.GetFieldsThaCanBeSetPrivately(methodSymbol.ContainingType);
                return(mappingEngine.MapUsingSimpleAssignment(generator, targets, sourceFinder));
            }

            if (SymbolHelper.IsMappingConstructor(methodSymbol))
            {
                var source       = methodSymbol.Parameters[0];
                var sourceFinder = new ObjectMembersMappingSourceFinder(source.Type, generator.IdentifierName(source.Name), generator);
                var targets      = ObjectHelper.GetFieldsThaCanBeSetFromConstructor(methodSymbol.ContainingType);
                return(mappingEngine.MapUsingSimpleAssignment(generator, targets, sourceFinder));
            }

            if (SymbolHelper.IsMultiParameterMappingConstructor(methodSymbol))
            {
                var sourceFinder = new LocalScopeMappingSourceFinder(semanticModel, methodSymbol.Parameters);
                var targets      = ObjectHelper.GetFieldsThaCanBeSetFromConstructor(methodSymbol.ContainingType);
                return(mappingEngine.MapUsingSimpleAssignment(generator, targets, sourceFinder));
            }
            return(Enumerable.Empty <SyntaxNode>());
        }
Пример #7
0
        internal SyntaxNode GetDefaultExpression(ITypeSymbol type, MappingPath mappingPath)
        {
            if (mappingPath.AddToMapped(type) == false)
            {
                return(syntaxGenerator.DefaultExpression(type)
                       .WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */")));
            }

            if (SymbolHelper.IsNullable(type, out var underlyingType))
            {
                type = underlyingType;
            }


            if (type.TypeKind == TypeKind.Enum && type is INamedTypeSymbol namedTypeSymbol)
            {
                var enumOptions = namedTypeSymbol.MemberNames.Where(x => x != "value__" && x != ".ctor").ToList();
                if (enumOptions.Count > 0)
                {
                    return(syntaxGenerator.MemberAccessExpression(syntaxGenerator.IdentifierName(namedTypeSymbol.Name), syntaxGenerator.IdentifierName(enumOptions[0])));
                }
                return(syntaxGenerator.DefaultExpression(type));
            }

            if (type.SpecialType == SpecialType.None)
            {
                var objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(type);


                if (MappingHelper.IsCollection(type))
                {
                    var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(type);

                    if (type is IArrayTypeSymbol)
                    {
                        objectCreationExpression = SyntaxFactory.ObjectCreationExpression((TypeSyntax)syntaxGenerator.TypeExpression(type));
                    }
                    else if (type.TypeKind == TypeKind.Interface || isReadonlyCollection)
                    {
                        var namedType = type as INamedTypeSymbol;
                        if (namedType.IsGenericType)
                        {
                            var typeArgumentListSyntax = SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(namedType.TypeArguments.Select(x => syntaxGenerator.TypeExpression(x))));
                            var newType = SyntaxFactory.GenericName(SyntaxFactory.Identifier("List"), typeArgumentListSyntax);
                            objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax));
                        }
                        else
                        {
                            var newType = SyntaxFactory.ParseTypeName("ArrayList");
                            objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax));
                        }
                    }
                    var subType = MappingHelper.GetElementType(type);
                    var initializationBlockExpressions = new SeparatedSyntaxList <ExpressionSyntax>();
                    var subTypeDefault = (ExpressionSyntax)GetDefaultExpression(subType, mappingPath.Clone());
                    if (subTypeDefault != null)
                    {
                        initializationBlockExpressions = initializationBlockExpressions.Add(subTypeDefault);
                    }

                    var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, initializationBlockExpressions).FixInitializerExpressionFormatting(objectCreationExpression);
                    return(objectCreationExpression
                           .WithInitializer(initializerExpressionSyntax)
                           .WrapInReadonlyCollectionIfNecessary(isReadonlyCollection, syntaxGenerator));
                }

                {
                    var nt = type as INamedTypeSymbol;

                    if (nt == null)
                    {
                        var genericTypeConstraints = type.UnwrapGeneric().ToList();
                        if (genericTypeConstraints.Any() == false)
                        {
                            return(GetDefaultForUnknown(type, SyntaxFactory.ParseTypeName("object")));
                        }
                        nt = genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Class) as INamedTypeSymbol ??
                             genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Interface) as INamedTypeSymbol;
                    }

                    if (nt == null)
                    {
                        return(GetDefaultForUnknownType(type));
                    }

                    if (nt.TypeKind == TypeKind.Interface)
                    {
                        var implementations     = SymbolFinder.FindImplementationsAsync(type, this._document.Project.Solution).Result;
                        var firstImplementation = implementations.FirstOrDefault();
                        if (firstImplementation is INamedTypeSymbol == false)
                        {
                            return(GetDefaultForUnknownType(type));
                        }

                        nt = firstImplementation as INamedTypeSymbol;
                        objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt);
                    }
                    else if (nt.TypeKind == TypeKind.Class && nt.IsAbstract)
                    {
                        var randomDerived = SymbolFinder.FindDerivedClassesAsync(nt, this._document.Project.Solution).Result
                                            .FirstOrDefault(x => x.IsAbstract == false);

                        if (randomDerived != null)
                        {
                            nt = randomDerived;
                            objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt);
                        }
                    }

                    var publicConstructors = nt.Constructors.Where(x =>
                                                                   x.DeclaredAccessibility == Accessibility.Public ||
                                                                   (x.DeclaredAccessibility == Accessibility.Internal &&
                                                                    x.ContainingAssembly.IsSameAssemblyOrHasFriendAccessTo(_contextAssembly))).ToList();


                    var hasDefaultConstructor = publicConstructors.Any(x => x.Parameters.Length == 0);
                    if (hasDefaultConstructor == false && publicConstructors.Count > 0)
                    {
                        var randomConstructor    = publicConstructors.First();
                        var constructorArguments = randomConstructor.Parameters.Select(p => GetDefaultExpression(p.Type, mappingPath.Clone())).ToList();
                        objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt, constructorArguments);
                    }

                    var fields      = ObjectHelper.GetFieldsThaCanBeSetPublicly(nt, _contextAssembly);
                    var assignments = fields.Select(x =>
                    {
                        var identifier = (ExpressionSyntax)(SyntaxFactory.IdentifierName(x.Name));
                        return((ExpressionSyntax)syntaxGenerator.AssignmentStatement(identifier, this.FindMappingSource(x.Type, mappingPath.Clone()).Expression));
                    }).ToList();

                    if (assignments.Count == 0)
                    {
                        return(objectCreationExpression);
                    }
                    var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, new SeparatedSyntaxList <ExpressionSyntax>().AddRange(assignments)).FixInitializerExpressionFormatting(objectCreationExpression);
                    return(objectCreationExpression.WithInitializer(initializerExpressionSyntax));
                }
            }


            return(GetDefaultForSpecialType(type));
        }
Пример #8
0
 private bool IsUnwrappingNeeded(ITypeSymbol targetType, MappingElement element)
 {
     return(targetType.Equals(element.ExpressionType) == false && (ObjectHelper.IsSimpleType(targetType) || SymbolHelper.IsNullable(targetType, out _)));
 }