private TypeDeclarationSyntax TransformProperty(PropertyTransformationResult propertyTransform, TypeDeclarationSyntax newTypeNode, TypeTransformationResult transformResult, INamespaceTransformationMetadata namespaceMetadata, SyntaxTrivia memberWhitespace, bool onlyMissingMembers) { var propertyNode = newTypeNode.GetAnnotatedNodes(propertyTransform.Annotation) .OfType <PropertyDeclarationSyntax>() .First(); var transformedNode = TransformProperty(propertyNode, !onlyMissingMembers, propertyTransform, transformResult, namespaceMetadata); foreach (var transformedAccessor in transformedNode.TransformedAccessors.Where(o => o.Transformed != null)) { foreach (var transformer in _configuration.MethodTransformers) { var methodTransformResult = transformer.Transform(transformedAccessor, transformResult, namespaceMetadata); if (methodTransformResult == MethodTransformerResult.Skip) { continue; } transformedAccessor.Transformed = methodTransformResult.TransformedNode ?? transformedAccessor.Transformed; if (methodTransformResult.Fields != null) { if (transformedNode.Fields == null) { transformedNode.Fields = new List <FieldDeclarationSyntax>(1); } transformedNode.Fields.AddRange(methodTransformResult.Fields); // Update member names for next transformators foreach (var variable in methodTransformResult.Fields.SelectMany(o => o.Declaration.Variables)) { transformResult.MemberNames = transformResult.MemberNames.Add(variable.Identifier.Text); } } if (methodTransformResult.Methods != null) { transformedNode.AddMethods(methodTransformResult.Methods); // Update member names for next transformators foreach (var method in methodTransformResult.Methods) { transformResult.MemberNames = transformResult.MemberNames.Add(method.Identifier.Text); } } } propertyTransform.AddMethod(transformedAccessor.Transformed); } newTypeNode = newTypeNode.AppendMembers(propertyNode, propertyTransform.Fields, propertyTransform.Methods); // We need to remove the property when generating only the missing members if (onlyMissingMembers || propertyTransform.AnalyzationResult.Conversion == PropertyConversion.Ignore) { // We need to add a whitespace trivia to keep directives as they will not have any leading whitespace newTypeNode = newTypeNode.RemoveNodeKeepDirectives(propertyTransform.Annotation, memberWhitespace, transformResult.EndOfLineTrivia); } return(newTypeNode); }
private RootTypeTransformationResult TransformType(ITypeAnalyzationResult rootTypeResult, INamespaceTransformationMetadata namespaceMetadata, bool onlyMissingMembers) { var rootTypeNode = rootTypeResult.Node; var startRootTypeSpan = rootTypeNode.SpanStart; var rootTransformResult = new RootTypeTransformationResult(rootTypeResult) { MemberNames = rootTypeResult.Symbol.MemberNames.ToImmutableHashSet() }; // We do this here because we want that the root node has span start equal to 0 rootTypeNode = rootTypeNode.WithAdditionalAnnotations(new SyntaxAnnotation(rootTransformResult.Annotation)); startRootTypeSpan -= rootTypeNode.SpanStart; // Before any modification we need to annotate nodes that will be transformed in order to find them later on. // We cannot rely on spans as they changes each time the node is modified. // We need to annotate also the ignored types to be later removed foreach (var typeResult in rootTypeResult.GetSelfAndDescendantsTypes()) { var typeSpanStart = typeResult.Node.SpanStart - startRootTypeSpan; var typeSpanLength = typeResult.Node.Span.Length; var typeNode = rootTypeNode.DescendantNodesAndSelf().OfType <TypeDeclarationSyntax>() .First(o => o.SpanStart == typeSpanStart && o.Span.Length == typeSpanLength); var leadingWhitespace = typeNode.GetLeadingWhitespace(); TypeTransformationResult transformResult; if (typeNode == rootTypeNode) { transformResult = rootTransformResult; transformResult.IndentTrivia = typeNode.GetIndent(leadingWhitespace, namespaceMetadata.LeadingWhitespaceTrivia); } else { transformResult = new TypeTransformationResult(typeResult) { MemberNames = typeResult.Symbol.MemberNames.ToImmutableHashSet(), IndentTrivia = typeNode.GetIndent(leadingWhitespace) }; rootTypeNode = rootTypeNode.ReplaceNode(typeNode, typeNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformResult.Annotation))); rootTransformResult.DescendantTransformedTypes.Add(transformResult); } transformResult.LeadingWhitespaceTrivia = leadingWhitespace; transformResult.EndOfLineTrivia = typeNode.GetEndOfLine(); if (typeResult.Conversion == TypeConversion.Ignore) { continue; // The ignored type shall be only annotated } // TypeReferences can be changes only if we create a new type if (typeResult.Conversion == TypeConversion.NewType || typeResult.Conversion == TypeConversion.Copy) { foreach (var typeReference in typeResult.TypeReferences.Where(o => o.TypeAnalyzationResult.Conversion == TypeConversion.NewType)) { var reference = typeReference.ReferenceLocation; var refSpanStart = reference.Location.SourceSpan.Start - startRootTypeSpan; var refSpanLength = reference.Location.SourceSpan.Length; var nameNode = rootTypeNode.GetSimpleName(refSpanStart, refSpanLength, typeReference.IsCref); var transformedNode = new TransformationResult(nameNode) { Transformed = nameNode.WithIdentifier(Identifier(nameNode.Identifier.ValueText + "Async").WithTriviaFrom(nameNode.Identifier)) }; transformResult.TransformedNodes.Add(transformedNode); rootTypeNode = rootTypeNode.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } } foreach (var fieldResult in typeResult.Fields) { var fieldSpanStart = fieldResult.Node.SpanStart - startRootTypeSpan; var fieldSpanLength = fieldResult.Node.Span.Length; var fieldNode = rootTypeNode.DescendantNodes() .OfType <BaseFieldDeclarationSyntax>() .First(o => o.SpanStart == fieldSpanStart && o.Span.Length == fieldSpanLength); var transformedNode = new FieldTransformationResult(fieldResult); transformResult.TransformedFields.Add(transformedNode); rootTypeNode = rootTypeNode.ReplaceNode(fieldNode, fieldNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } // Annotate and save an empty method transformation result foreach (var methodResult in typeResult.Methods /*.Where(o => o.Conversion != MethodConversion.Ignore)*/) { var methodSpanStart = methodResult.Node.SpanStart - startRootTypeSpan; var methodSpanLength = methodResult.Node.Span.Length; var methodNode = rootTypeNode.DescendantNodes() .OfType <MethodDeclarationSyntax>() .First(o => o.SpanStart == methodSpanStart && o.Span.Length == methodSpanLength); // Only create the transformation result for the method and transform the method later as the method may change // (a directive may be added to the method when removing type members) var transformedNode = new MethodTransformationResult(methodResult); transformResult.TransformedMethods.Add(transformedNode); rootTypeNode = rootTypeNode.ReplaceNode(methodNode, methodNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); // Annotate all locks inside the method before the transformations begins as will be easier to transform them if needed foreach (var lockData in methodResult.Locks) { var lockSpanStart = lockData.Node.SpanStart - startRootTypeSpan; var lockSpanLength = lockData.Node.Span.Length; var lockNode = rootTypeNode.DescendantNodes() .OfType <LockStatementSyntax>() .First(o => o.SpanStart == lockSpanStart && o.Span.Length == lockSpanLength); var lockTransformedNode = new LockTransformationResult(lockData); transformedNode.TransformedLocks.Add(lockTransformedNode); rootTypeNode = rootTypeNode.ReplaceNode(lockNode, lockNode.WithAdditionalAnnotations(new SyntaxAnnotation(lockTransformedNode.Annotation))); } } foreach (var methodResult in typeResult.SpecialMethods) { var methodSpanStart = methodResult.GetNode().SpanStart - startRootTypeSpan; var methodSpanLength = methodResult.GetNode().Span.Length; var methodNode = rootTypeNode.DescendantNodes() .OfType <BaseMethodDeclarationSyntax>() .First(o => o.SpanStart == methodSpanStart && o.Span.Length == methodSpanLength); var transformedNode = new FunctionTransformationResult(methodResult); transformResult.TransformedSpecialMethods.Add(transformedNode); rootTypeNode = rootTypeNode.ReplaceNode(methodNode, methodNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } foreach (var propertyResult in typeResult.Properties) { var spanStart = propertyResult.Node.SpanStart - startRootTypeSpan; var spanLength = propertyResult.Node.Span.Length; var node = rootTypeNode.DescendantNodes() .OfType <PropertyDeclarationSyntax>() .First(o => o.SpanStart == spanStart && o.Span.Length == spanLength); var transformedNode = new PropertyTransformationResult(propertyResult); transformResult.TransformedProperties.Add(transformedNode); rootTypeNode = rootTypeNode.ReplaceNode(node, node.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } } // Save the orignal node that was only annotated var originalAnnotatedNode = rootTypeNode; // Now we can start transforming the type. Start from the bottom in order to preserve replaced nested types foreach (var transformResult in rootTransformResult.GetSelfAndDescendantTransformedTypes().OrderByDescending(o => o.OriginalNode.SpanStart)) { var typeResult = transformResult.AnalyzationResult; // Add partial keyword on the original node if not present if ((typeResult.Conversion == TypeConversion.Partial || onlyMissingMembers) && !typeResult.IsPartial) { if (rootTransformResult.OriginalModified == null) { rootTransformResult.OriginalModified = originalAnnotatedNode; } var typeNode = rootTransformResult.OriginalModified.GetAnnotatedNodes(transformResult.Annotation).OfType <TypeDeclarationSyntax>().First(); rootTransformResult.OriginalModified = rootTransformResult.OriginalModified.ReplaceNode(typeNode, typeNode.AddPartial()); } if (typeResult.Conversion == TypeConversion.Ignore || ( onlyMissingMembers && !typeResult.GetSelfAndDescendantsTypes().SelectMany(o => o.MethodsAndAccessors).Any(o => o.Missing) )) { rootTypeNode = rootTypeNode.RemoveNodeKeepDirectives(transformResult.Annotation, transformResult.LeadingWhitespaceTrivia, transformResult.EndOfLineTrivia); continue; } var memberWhitespace = Whitespace(transformResult.LeadingWhitespaceTrivia.ToFullString() + transformResult.IndentTrivia.ToFullString()); if (typeResult.Conversion == TypeConversion.Partial || onlyMissingMembers) { // First we need to remove ignored method var typeNode = rootTypeNode.GetAnnotatedNodes(transformResult.Annotation).OfType <TypeDeclarationSyntax>().First(); // We need to remove the attributes as they cannot be defined in both partial classes var newTypeNode = typeNode.AddPartial().WithoutAttributes(); // We need to remove all other members that are not methods, properties or types newTypeNode = newTypeNode.RemoveMembersKeepDirectives(o => !(o is BaseMethodDeclarationSyntax || o is TypeDeclarationSyntax || o is PropertyDeclarationSyntax || o is BaseFieldDeclarationSyntax), memberWhitespace, transformResult.EndOfLineTrivia); rootTypeNode = TransformTypeMembers(namespaceMetadata, onlyMissingMembers, transformResult, newTypeNode, memberWhitespace, rootTypeNode, typeNode); } // If the root type has to be a new type then all nested types have to be new types else if (typeResult.Conversion == TypeConversion.NewType || typeResult.Conversion == TypeConversion.Copy) { var typeNode = rootTypeNode.GetAnnotatedNodes(transformResult.Annotation).OfType <TypeDeclarationSyntax>().First(); var identifierToken = typeNode.ChildTokens().First(o => o.IsKind(SyntaxKind.IdentifierToken)); var newTypeNode = typeResult.Conversion == TypeConversion.NewType ? typeNode.ReplaceToken(identifierToken, Identifier(identifierToken.ValueText + "Async").WithTriviaFrom(identifierToken)) : typeNode; rootTypeNode = TransformTypeMembers(namespaceMetadata, onlyMissingMembers, transformResult, newTypeNode, memberWhitespace, rootTypeNode, typeNode); } } rootTransformResult.Transformed = rootTypeNode; return(rootTransformResult); }
private void TransformPropertyAccessor(SyntaxNode node, PropertyTransformationResult propertyResult, AccessorTransformationResult result, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = result.AnalyzationResult; result.BodyLeadingWhitespaceTrivia = propertyResult.BodyLeadingWhitespaceTrivia; result.LeadingWhitespaceTrivia = propertyResult.LeadingWhitespaceTrivia; result.EndOfLineTrivia = propertyResult.EndOfLineTrivia; result.IndentTrivia = propertyResult.IndentTrivia; var methodConversion = methodResult.Conversion; if (!methodConversion.HasFlag(MethodConversion.ToAsync)) { return; } var methodNode = MethodDeclaration( methodResult.Symbol.MethodKind != MethodKind.PropertySet ? propertyResult.OriginalNode.Type : IdentifierName(nameof(Task)).WithTriviaFrom(propertyResult.OriginalNode.Type), methodResult.AsyncCounterpartName ) .WithModifiers(propertyResult.OriginalNode.Modifiers) .WithLeadingTrivia(propertyResult.OriginalNode.GetLeadingTrivia()); var methodBodyNode = methodResult.GetBodyNode(); if (methodBodyNode == null) { methodNode = methodNode .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(propertyResult.EndOfLineTrivia))); result.Transformed = methodNode; return; } var startMethodSpan = methodResult.Node.Span.Start; node = node.WithAdditionalAnnotations(new SyntaxAnnotation(result.Annotation)); startMethodSpan -= node.SpanStart; // First we need to annotate nodes that will be modified in order to find them later on. // We cannot rely on spans after the first modification as they will change var typeReferencesAnnotations = new List <string>(); foreach (var typeReference in methodResult.TypeReferences.Where(o => o.TypeAnalyzationResult.Conversion == TypeConversion.NewType)) { var reference = typeReference.ReferenceLocation; var startSpan = reference.Location.SourceSpan.Start - startMethodSpan; var nameNode = node.GetSimpleName(startSpan, reference.Location.SourceSpan.Length); var annotation = Guid.NewGuid().ToString(); node = node.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(annotation))); typeReferencesAnnotations.Add(annotation); } foreach (var childFunction in methodResult.ChildFunctions.Where(o => o.Conversion != MethodConversion.Ignore)) { var functionNode = childFunction.GetNode(); var functionKind = functionNode.Kind(); var typeSpanStart = functionNode.SpanStart - startMethodSpan; var typeSpanLength = functionNode.Span.Length; var funcNode = node.DescendantNodesAndSelf() .First(o => o.IsKind(functionKind) && o.SpanStart == typeSpanStart && o.Span.Length == typeSpanLength); var transformFuncResult = TransformFunction(childFunction, result, typeMetadata, namespaceMetadata); result.TransformedFunctions.Add(transformFuncResult); node = node.ReplaceNode(funcNode, funcNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformFuncResult.Annotation))); } foreach (var referenceResult in methodResult.FunctionReferences .Where(o => o.GetConversion() == ReferenceConversion.ToAsync)) { var transfromReference = new FunctionReferenceTransformationResult(referenceResult); var isCref = referenceResult.IsCref; var reference = referenceResult.ReferenceLocation; var startSpan = reference.Location.SourceSpan.Start - startMethodSpan; var nameNode = node.GetSimpleName(startSpan, reference.Location.SourceSpan.Length, isCref); node = node.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(transfromReference.Annotation))); result.TransformedFunctionReferences.Add(transfromReference); if (isCref || referenceResult.IsNameOf || !methodResult.OmitAsync) { continue; } // We need to annotate the reference node (InvocationExpression, IdentifierName) in order to know if we need to wrap the node in a Task.FromResult var refNode = referenceResult.ReferenceNode; var bodyReference = (IBodyFunctionReferenceAnalyzationResult)referenceResult; if (bodyReference.UseAsReturnValue || refNode.IsReturned()) { startSpan = refNode.SpanStart - startMethodSpan; var referenceNode = node.DescendantNodes().First(o => o.SpanStart == startSpan && o.Span.Length == refNode.Span.Length); node = node.ReplaceNode(referenceNode, referenceNode.WithAdditionalAnnotations(new SyntaxAnnotation(Annotations.TaskReturned))); } } // Modify references foreach (var refAnnotation in typeReferencesAnnotations) { var nameNode = node.GetAnnotatedNodes(refAnnotation).OfType <SimpleNameSyntax>().First(); node = node .ReplaceNode(nameNode, nameNode.WithIdentifier(Identifier(nameNode.Identifier.Value + "Async"))); } foreach (var transformFunction in result.TransformedFunctions) { var funcNode = node.GetAnnotatedNodes(transformFunction.Annotation).First(); node = node .ReplaceNode(funcNode, transformFunction.Transformed); } // We have to order by OriginalStartSpan in order to have consistent formatting when adding awaits foreach (var transfromReference in result.TransformedFunctionReferences.OrderByDescending(o => o.OriginalStartSpan)) { node = TransformFunctionReference(node, methodResult, transfromReference, typeMetadata, namespaceMetadata); } if (methodResult.Symbol.MethodKind == MethodKind.PropertySet) { methodNode = methodNode.WithParameterList( methodNode.ParameterList.WithParameters( SingletonSeparatedList( Parameter(Identifier(TriviaList(), "value", TriviaList())) .WithType(propertyResult.OriginalNode.Type.WithoutTrivia().WithTrailingTrivia(TriviaList(Space))) ) ) ); } if (node is ArrowExpressionClauseSyntax arrowNode) { methodNode = methodNode .WithExpressionBody(arrowNode) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(propertyResult.EndOfLineTrivia))); } else if (node is AccessorDeclarationSyntax accessorNode) { if (accessorNode.ExpressionBody != null) { methodNode = methodNode .WithExpressionBody(accessorNode.ExpressionBody) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(propertyResult.EndOfLineTrivia))); } else { methodNode = methodNode .WithBody(accessorNode.Body); } } methodNode = FixupBodyFormatting(methodNode, result); result.Transformed = methodNode; }
private PropertyTransformationResult TransformProperty(PropertyDeclarationSyntax propertyNode, bool canCopy, PropertyTransformationResult result, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var analyzeResult = result.AnalyzationResult; var startRootSpan = analyzeResult.Node.SpanStart; // Calculate whitespace method trivias result.EndOfLineTrivia = propertyNode.GetEndOfLine(); result.LeadingWhitespaceTrivia = propertyNode.GetLeadingWhitespace(); result.IndentTrivia = propertyNode.GetIndent(result.LeadingWhitespaceTrivia, typeMetadata.LeadingWhitespaceTrivia); result.BodyLeadingWhitespaceTrivia = Whitespace(result.LeadingWhitespaceTrivia.ToFullString() + result.IndentTrivia.ToFullString()); if (analyzeResult.Conversion == PropertyConversion.Ignore && analyzeResult.GetAccessors().All(o => o.Conversion == MethodConversion.Ignore)) { return(result); } propertyNode = propertyNode.WithAdditionalAnnotations(new SyntaxAnnotation(result.Annotation)); startRootSpan -= propertyNode.SpanStart; foreach (var accessorResult in analyzeResult.GetAccessors().Where(o => o.Conversion != MethodConversion.Ignore)) { var spanStart = accessorResult.Node.SpanStart - startRootSpan; var spanLength = accessorResult.Node.Span.Length; var accessorNode = propertyNode.DescendantNodes() .First(o => o.SpanStart == spanStart && o.Span.Length == spanLength); var transformedNode = new AccessorTransformationResult(accessorResult); result.TransformedAccessors.Add(transformedNode); propertyNode = propertyNode.ReplaceNode(accessorNode, accessorNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } if (canCopy && result.AnalyzationResult.Conversion == PropertyConversion.Copy) { result.Transformed = result.OriginalNode; } foreach (var accessorResult in result.TransformedAccessors.OrderByDescending(o => o.OriginalStartSpan)) { var accessorNode = propertyNode.GetAnnotatedNodes(accessorResult.Annotation) .First(); TransformPropertyAccessor(accessorNode, result, accessorResult, typeMetadata, namespaceMetadata); } return(result); }