// TODO: Missing members private TypeTransformationResult TransformType(ITypeAnalyzationResult rootTypeResult) { var rootTypeNode = rootTypeResult.Node; var startRootTypeSpan = rootTypeNode.SpanStart; var typeResultMetadatas = new Dictionary <ITypeAnalyzationResult, TypeTransformationMetadata>(); var rootMetadata = new TypeTransformationMetadata { ReservedFieldNames = new HashSet <string>(rootTypeResult.Symbol.MemberNames) }; // We do this here because we want that the root node has span start equal to 0 rootTypeNode = rootTypeNode.WithAdditionalAnnotations(new SyntaxAnnotation(rootMetadata.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. foreach (var typeResult in rootTypeResult.GetSelfAndDescendantsTypes(o => o.Conversion != TypeConversion.Ignore)) { 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); TypeTransformationMetadata metadata; if (typeNode == rootTypeNode) { metadata = rootMetadata; } else { metadata = new TypeTransformationMetadata { ReservedFieldNames = new HashSet <string>(typeResult.Symbol.MemberNames) }; rootTypeNode = rootTypeNode.ReplaceNode(typeNode, typeNode.WithAdditionalAnnotations(new SyntaxAnnotation(metadata.Annotation))); } // TypeReferences can be changes only if we create a new type if (rootTypeResult.Conversion == TypeConversion.NewType) { foreach (var typeReference in typeResult.TypeReferences) { var reference = typeReference.ReferenceLocation; var refSpanStart = reference.Location.SourceSpan.Start - startRootTypeSpan; var refSpanLength = reference.Location.SourceSpan.Length; if (refSpanStart < 0) { // TODO: cref //var startSpan = reference.Location.SourceSpan.Start - rootTypeInfo.Node.GetLeadingTrivia().Span.Start; //var crefNode = leadingTrivia.First(o => o.SpanStart == startSpan && o.Span.Length == refSpanLength); continue; } var nameNode = rootTypeNode.GetSimpleName(refSpanStart, refSpanLength); var transformedNode = new TransformationResult(nameNode) { TransformedNode = nameNode.WithIdentifier(Identifier(nameNode.Identifier.ValueText + "Async")) }; metadata.TransformedNodes.Add(transformedNode); rootTypeNode = rootTypeNode.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } } // Annotate and save the transformed methods foreach (var methodResult in typeResult.Methods) { 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); var transformedNode = TransformMethod(metadata, methodResult); metadata.TransformedMethods.Add(transformedNode); rootTypeNode = rootTypeNode.ReplaceNode(methodNode, methodNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } typeResultMetadatas.Add(typeResult, metadata); } // Save the orignal node that was only annotated var result = new TypeTransformationResult(rootTypeNode); // Now we can start transforming the type. Start from the bottom in order to preserve replaced nested types foreach (var typeResult in rootTypeResult.GetSelfAndDescendantsTypes(o => o.Conversion != TypeConversion.Ignore) .OrderByDescending(o => o.Node.SpanStart)) { var metadata = typeResultMetadatas[typeResult]; // Add partial keyword on the original node if not present if (typeResult.Conversion == TypeConversion.Partial && !typeResult.IsPartial) { var typeNode = rootTypeNode.GetAnnotatedNodes(metadata.Annotation).OfType <TypeDeclarationSyntax>().First(); result.OriginalModifiedNode = result.Node.ReplaceNode(typeNode, typeNode.AddPartial()); } // If the root type has to be a new type then all nested types have to be new types if (typeResult.Conversion == TypeConversion.NewType) { // Replace all rewritten nodes foreach (var rewNode in metadata.TransformedNodes) { var node = rootTypeNode.GetAnnotatedNodes(rewNode.Annotation).First(); if (rewNode.TransformedNode == null) { //TODO: fix regions rootTypeNode = rootTypeNode.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia); } else { rootTypeNode = rootTypeNode.ReplaceNode(node, rewNode.TransformedNode); } } } else if (typeResult.Conversion == TypeConversion.Partial) { var typeNode = rootTypeNode.GetAnnotatedNodes(metadata.Annotation).OfType <TypeDeclarationSyntax>().First(); var newNodes = metadata.TransformedNodes .Union(metadata.TransformedMethods) .Where(o => o.TransformedNode != null) .OrderBy(o => o.Node.SpanStart) .Select(o => o.TransformedNode) .Union(typeNode.DescendantNodes().OfType <TypeDeclarationSyntax>()) .ToList(); if (!newNodes.Any()) { //TODO: fix regions rootTypeNode = rootTypeNode.RemoveNode(typeNode, SyntaxRemoveOptions.KeepNoTrivia); } else { // We need to remove the attributes as they cannot be defined in both partial classes var newTypeNode = typeNode.AddPartial().WithoutAttributes(); // Add fields for async lock if any. We need a lock field for each synchronized method foreach (var methodTransform in metadata.TransformedMethods.Where(o => o.AsyncLockField != null).OrderBy(o => o.Node.SpanStart)) { newNodes.Insert(0, methodTransform.AsyncLockField); } newTypeNode = newTypeNode.WithMembers(List(newNodes)); //TODO: fix regions rootTypeNode = rootTypeNode.ReplaceNode(typeNode, newTypeNode /*.RemoveLeadingDirectives()*/); } } } result.TransformedNode = rootTypeNode; return(result); }
private DocumentTransformationResult TransformDocument(IDocumentAnalyzationResult documentResult) { var rootNode = documentResult.Node; var result = new DocumentTransformationResult(rootNode); var rewrittenNodes = new List <TransformationResult>(); var namespaceNodes = new List <MemberDeclarationSyntax>(); var hasTaskUsing = rootNode.Usings.Any(o => o.Name.ToString() == "System.Threading.Tasks"); foreach (var namespaceResult in documentResult.Namespaces.OrderBy(o => o.Node.SpanStart)) { var namespaceNode = namespaceResult.Node; var typeNodes = new List <MemberDeclarationSyntax>(); foreach (var typeResult in namespaceResult.Types.Where(o => o.Conversion != TypeConversion.Ignore).OrderBy(o => o.Node.SpanStart)) { var transformResult = TransformType(typeResult); if (transformResult.TransformedNode == null) { continue; } typeNodes.Add(transformResult.TransformedNode); // We need to update the original file if it was modified if (transformResult.OriginalModifiedNode != null) { var typeSpanStart = typeResult.Node.SpanStart; var typeSpanLength = typeResult.Node.Span.Length; var typeNode = rootNode.DescendantNodesAndSelf().OfType <TypeDeclarationSyntax>() .First(o => o.SpanStart == typeSpanStart && o.Span.Length == typeSpanLength); var rewritenNode = new TransformationResult(typeNode) { TransformedNode = transformResult.OriginalModifiedNode }; rootNode = rootNode.ReplaceNode(typeNode, typeNode.WithAdditionalAnnotations(new SyntaxAnnotation(rewritenNode.Annotation))); rewrittenNodes.Add(rewritenNode); } // TODO: missing members //if (typeInfo.TypeTransformation == TypeTransformation.NewType && typeInfo.HasMissingMembers) //{ // transformResult = TransformType(typeInfo, true); // if (transformResult.Node == null) // { // continue; // } // typeNodes.Add(transformResult.Node); //} } if (typeNodes.Any()) { var leadingTrivia = namespaceResult.Types.First().Node.GetFirstToken().LeadingTrivia.First(o => o.IsKind(SyntaxKind.WhitespaceTrivia)); //TODO: check if Task is conflicted inside namespace if (!hasTaskUsing && namespaceNode.Usings.All(o => o.Name.ToString() != "System.Threading.Tasks")) { namespaceNode = namespaceNode.AddUsing("System.Threading.Tasks", TriviaList(leadingTrivia)); } // TODO: add locking namespaces namespaceNodes.Add(namespaceNode .WithMembers(List(typeNodes))); } } if (!namespaceNodes.Any()) { return(result); } // Update the original node var origRootNode = rootNode; foreach (var rewrittenNode in rewrittenNodes) { origRootNode = rootNode.ReplaceNode(rootNode.GetAnnotatedNodes(rewrittenNode.Annotation).First(), rewrittenNode.TransformedNode); } if (rootNode != origRootNode) { result.OriginalModifiedNode = origRootNode; } // Create the new node rootNode = rootNode .WithMembers(List(namespaceNodes)); // Add auto-generated comment var token = rootNode.DescendantTokens().First(); rootNode = rootNode.ReplaceToken(token, token.AddAutoGeneratedTrivia()); result.TransformedNode = rootNode; return(result); }