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>()); }
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); }
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; } }
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; } }
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>()); }
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)); }
private bool IsUnwrappingNeeded(ITypeSymbol targetType, MappingElement element) { return(targetType.Equals(element.ExpressionType) == false && (ObjectHelper.IsSimpleType(targetType) || SymbolHelper.IsNullable(targetType, out _))); }