public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            var methodResult = transformResult.AnalyzationResult;

            if (!methodResult.Conversion.HasFlag(MethodConversion.ToAsync))
            {
                return(MethodTransformerResult.Skip);
            }
            var methodNode = transformResult.Transformed;

            if (methodNode.GetFunctionBody() == null)
            {
                return(Update(methodNode, methodResult, namespaceMetadata));
            }
            if (methodResult.SplitTail || methodResult.PreserveReturnType || !methodResult.OmitAsync)
            {
                if (!methodResult.OmitAsync)
                {
                    methodNode = methodNode.AddAsync();
                }
                return(Update(methodNode, methodResult, namespaceMetadata));
            }
            var rewriter = new ReturnTaskFunctionRewriter(transformResult, namespaceMetadata);

            methodNode = (MethodDeclarationSyntax)rewriter.VisitMethodDeclaration(methodNode);
            return(Update(methodNode, methodResult, namespaceMetadata));
        }
        private DocumentationCommentTriviaSyntax ProcessTag(
            DocumentationCommentTriviaSyntax documentationNode,
            IMethodOrAccessorTransformationResult methodTransformResult,
            string tagName,
            Func <IMethodSymbol, string> addOrReplace,
            Predicate <IMethodSymbol> canRemove,
            bool prepend)
        {
            if (addOrReplace == null && canRemove == null)
            {
                return(documentationNode);
            }
            var methodSymbol = methodTransformResult.AnalyzationResult.Symbol;
            var tagContent   = addOrReplace?.Invoke(methodSymbol);
            var removeTag    = canRemove?.Invoke(methodSymbol);
            var tagNode      = documentationNode.Content.OfType <XmlElementSyntax>()
                               .FirstOrDefault(o => o.StartTag.Name.ToString() == tagName);

            if (!string.IsNullOrEmpty(tagContent))
            {
                var indent     = methodTransformResult.LeadingWhitespaceTrivia.ToFullString();
                var eol        = methodTransformResult.EndOfLineTrivia.ToFullString();
                var tagComment = CreateTagContent(tagName, tagContent, indent, eol);

                // When prepending a new tag before an existing tag we have to add the indentation at the end otherwise on start
                if (prepend && tagNode == null && documentationNode.Content.Any())
                {
                    var lastComment = tagComment.Content.Last();
                    tagComment = tagComment.ReplaceNode(lastComment, lastComment
                                                        .WithTrailingTrivia(lastComment.GetTrailingTrivia()
                                                                            .Add(methodTransformResult.LeadingWhitespaceTrivia)));
                }
                else
                {
                    var startComment = tagComment.Content.First();
                    tagComment = tagComment.ReplaceNode(startComment, startComment
                                                        .WithLeadingTrivia(DocumentationCommentExterior($"{indent}///")));
                }

                if (tagNode != null)
                {
                    documentationNode = documentationNode.ReplaceNode(tagNode, tagComment.Content.OfType <XmlElementSyntax>().First());
                }
                else
                {
                    documentationNode = documentationNode.WithContent(prepend
                                                ? documentationNode.Content.InsertRange(0, tagComment.Content)
                                                : documentationNode.Content.AddRange(tagComment.Content));
                }
            }
            else if (removeTag == true && tagNode != null)
            {
                // We need to remove the "///" XmlText and the tag itself
                var index = documentationNode.Content.IndexOf(tagNode);
                documentationNode = documentationNode.RemoveNode(tagNode, SyntaxRemoveOptions.KeepNoTrivia);
                documentationNode = documentationNode.RemoveNode(documentationNode.Content[index - 1], SyntaxRemoveOptions.KeepNoTrivia);
            }
            return(documentationNode);
        }
Exemplo n.º 3
0
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            if (transformResult.AnalyzationResult.OmitAsync)
            {
                return(MethodTransformerResult.Skip);
            }

            var result = _rewriter.VisitMethod(transformResult.Transformed);

            return(result == null
                                ? MethodTransformerResult.Skip
                                : MethodTransformerResult.Update(result));
        }
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult methodTransformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            if (!_isEnabled)
            {
                return(MethodTransformerResult.Skip);
            }

            var leadingTrivia        = methodTransformResult.Transformed.GetLeadingTrivia();
            var documentation        = leadingTrivia.FirstOrDefault(o => o.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia));
            var documentationNode    = (DocumentationCommentTriviaSyntax)documentation.GetStructure();
            var newDocumentationNode = documentationNode ??
                                       DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia);

            // Summary
            newDocumentationNode = ProcessTag(
                newDocumentationNode,
                methodTransformResult,
                "summary",
                _configuration.AddOrReplaceMethodSummary,
                _configuration.CanRemoveMethodSummary, true);

            // Remarks
            newDocumentationNode = ProcessTag(
                newDocumentationNode,
                methodTransformResult,
                "remarks",
                _configuration.AddOrReplaceMethodRemarks,
                _configuration.CanRemoveMethodRemarks, false);


            var transformed = methodTransformResult.Transformed;

            if (documentationNode != null)
            {
                transformed = transformed.ReplaceNode(documentationNode, newDocumentationNode);
            }
            else
            {
                // We have to append our documentation before the whitespace trivia
                var whitespaceIndex = leadingTrivia.IndexOf(SyntaxKind.WhitespaceTrivia);
                if (whitespaceIndex <= 0)
                {
                    whitespaceIndex = 0;
                }
                transformed = transformed.WithLeadingTrivia(transformed.GetLeadingTrivia().Insert(whitespaceIndex, Trivia(newDocumentationNode)));
            }

            return(MethodTransformerResult.Update(transformed));
        }
Exemplo n.º 5
0
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            var methodResult = transformResult.AnalyzationResult;

            if (!methodResult.HasYields || !methodResult.Conversion.HasFlag(MethodConversion.ToAsync))
            {
                return(MethodTransformerResult.Skip);
            }
            var methodNode = transformResult.Transformed;
            var rewriter   = new YieldRewriter(transformResult);

            methodNode = (MethodDeclarationSyntax)rewriter.VisitMethodDeclaration(methodNode);
            return(MethodTransformerResult.Update(methodNode));
        }
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            var methodResult = transformResult.AnalyzationResult;
            var methodNode   = transformResult.Transformed;

            if (
                !methodResult.Conversion.HasFlag(MethodConversion.ToAsync) ||
                methodResult.OmitAsync ||
                !methodResult.CancellationTokenRequired ||
                methodNode.GetFunctionBody() == null)
            {
                return(MethodTransformerResult.Skip);
            }

            var rewriter = new OperationCanceledExceptionFunctionRewriter(transformResult.EndOfLineTrivia, namespaceMetadata);

            methodNode = (MethodDeclarationSyntax)rewriter.VisitMethodDeclaration(methodNode);
            return(MethodTransformerResult.Update(methodNode));
        }
        private MethodDeclarationSyntax FixupBodyFormatting(MethodDeclarationSyntax methodNode, IMethodOrAccessorTransformationResult result)
        {
            var methodBody = methodNode.Body;

            if (methodBody == null)
            {
                if (methodNode.ExpressionBody == null)
                {
                    return(methodNode);
                }
                // Add space after the close paren token
                if (string.IsNullOrEmpty(methodNode.ParameterList.CloseParenToken.TrailingTrivia.ToFullString()))
                {
                    methodNode = methodNode.ReplaceToken(methodNode.ParameterList.CloseParenToken,
                                                         methodNode.ParameterList.CloseParenToken.WithTrailingTrivia(TriviaList(Space)));
                }

                return(methodNode);
            }
            // Make a diff by using only the whitespaces
            var triviaLengthDiff = GetLeadingWhitespaceDifference(methodBody, methodNode);

            if (triviaLengthDiff > 0)
            {
                // Normalize leading trivia
                methodNode = methodNode.WithBody(methodBody
                                                 .SubtractIndent(string.Join("", methodBody.GetLeadingTrivia()
                                                                             .Where(o => o.IsKind(SyntaxKind.WhitespaceTrivia))
                                                                             .Select(o => o.ToFullString())).Substring(0, triviaLengthDiff.Value)));
            }

            var eol = result.EndOfLineTrivia.ToFullString();

            // Add end of line for the close paren token if missing
            if (!methodNode.ConstraintClauses.Any() && !methodNode.ParameterList.CloseParenToken.TrailingTrivia.ToFullString().Contains(eol))
            {
                methodNode = methodNode.ReplaceToken(methodNode.ParameterList.CloseParenToken,
                                                     methodNode.ParameterList.CloseParenToken.WithTrailingTrivia(result.EndOfLineTrivia));
                methodNode = methodNode.ReplaceToken(methodNode.Body.OpenBraceToken,
                                                     methodNode.Body.OpenBraceToken.WithLeadingTrivia(result.LeadingWhitespaceTrivia));
            }
            else if (methodNode.ConstraintClauses.Any() && !methodNode.ConstraintClauses.Last().GetTrailingTrivia().ToFullString().Contains(eol))
            {
                var lastConstraint = methodNode.ConstraintClauses.Last();
                methodNode = methodNode.ReplaceNode(lastConstraint, lastConstraint.WithTrailingTrivia(result.EndOfLineTrivia));
                methodNode = methodNode.ReplaceToken(methodNode.Body.OpenBraceToken,
                                                     methodNode.Body.OpenBraceToken.WithLeadingTrivia(result.LeadingWhitespaceTrivia));
            }
            methodBody = methodNode.Body;
            var getLineSpan = methodBody.GetLocation().GetLineSpan().Span;

            // Add end of line tokens for open brace and statements when the whole block is written in one line (eg. { DoSomething(); })
            if (getLineSpan.End.Line == getLineSpan.Start.Line)
            {
                methodBody = methodBody.ReplaceToken(methodBody.OpenBraceToken,
                                                     methodBody.OpenBraceToken.WithTrailingTrivia(result.EndOfLineTrivia));
                // We have to fix also the statements leading trivia
                for (var i = 0; i < methodBody.Statements.Count; i++)
                {
                    var statement = methodBody.Statements[i];
                    methodBody = methodBody.ReplaceNode(statement, statement
                                                        .WithLeadingTrivia(result.BodyLeadingWhitespaceTrivia)
                                                        .WithTrailingTrivia(TriviaList(result.EndOfLineTrivia)));
                }
                methodBody = methodBody.ReplaceToken(methodBody.CloseBraceToken,
                                                     methodBody.CloseBraceToken.WithLeadingTrivia(TriviaList(result.LeadingWhitespaceTrivia)));
                methodNode = methodNode.WithBody(methodBody);
            }
            return(methodNode);
        }
Exemplo n.º 8
0
 public YieldRewriter(IMethodOrAccessorTransformationResult transformResult)
 {
     _transformResult = transformResult;
 }
Exemplo n.º 9
0
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            var methodResult = transformResult.AnalyzationResult;

            if (!methodResult.CancellationTokenRequired)
            {
                return(MethodTransformerResult.Skip);
            }
            var originalNode = methodResult.GetNode();
            var cancellationTokenParamName = "cancellationToken";             // TODO: handle variable collision for token
            var generationOptions          = methodResult.MethodCancellationToken.GetValueOrDefault();
            var methodNode = transformResult.Transformed;

            methodNode = methodNode
                         .AddCancellationTokenParameter(cancellationTokenParamName,
                                                        generationOptions.HasFlag(MethodCancellationToken.Optional),
                                                        transformResult.LeadingWhitespaceTrivia,
                                                        transformResult.EndOfLineTrivia);

            var methodBody = methodNode.Body;

            if (_configuration.Guards && methodBody != null && !methodResult.Faulted)
            {
                var startGuard = methodResult.OmitAsync
                                        ? GetSyncGuard(methodResult, cancellationTokenParamName, transformResult.BodyLeadingWhitespaceTrivia,
                                                       transformResult.EndOfLineTrivia, transformResult.IndentTrivia)
                                        : GetAsyncGuard(cancellationTokenParamName, transformResult.BodyLeadingWhitespaceTrivia,
                                                        transformResult.EndOfLineTrivia);

                methodNode = methodNode.WithBody(
                    methodBody.WithStatements(
                        methodBody.Statements.Insert(methodResult.Preconditions.Count, startGuard))
                    );
                // We need to get all statements that have at least one async invocation without a cancellation token argument, to prepend an extra guard
                var statements = new Dictionary <int, string>();
                foreach (var functionReference in transformResult.TransformedFunctionReferences)
                {
                    if (!(functionReference.AnalyzationResult is IBodyFunctionReferenceAnalyzationResult bodyFunctionReference))
                    {
                        continue;
                    }
                    if (bodyFunctionReference.GetConversion() != ReferenceConversion.ToAsync || bodyFunctionReference.PassCancellationToken)
                    {
                        continue;
                    }
                    var statement = methodNode
                                    .GetAnnotatedNodes(functionReference.Annotation)
                                    .First().Ancestors().OfType <StatementSyntax>().First();
                    if (statements.ContainsKey(statement.SpanStart))
                    {
                        continue;
                    }
                    var annotation = Guid.NewGuid().ToString();
                    methodNode = methodNode
                                 .ReplaceNode(statement, statement.WithAdditionalAnnotations(new SyntaxAnnotation(annotation)));
                    statements.Add(statement.SpanStart, annotation);
                }
                // For each statement we need to find the index where is located in the block.
                // TODO: Add support when the parent is not a block syntax
                foreach (var pair in statements)
                {
                    var statement   = methodNode.GetAnnotatedNodes(pair.Value).OfType <StatementSyntax>().First();
                    var parentBlock = statement.Parent as BlockSyntax;
                    if (parentBlock == null)
                    {
                        continue;                         // Currently not supported
                    }
                    var index          = parentBlock.Statements.IndexOf(statement);
                    var newParentBlock = parentBlock
                                         .WithStatements(parentBlock.Statements
                                                         .Insert(index, GetAsyncGuard(cancellationTokenParamName, statement.GetLeadingWhitespace(), transformResult.EndOfLineTrivia)));
                    methodNode = methodNode
                                 .ReplaceNode(parentBlock, newParentBlock);
                }
            }

            var originalMethodNode = originalNode as MethodDeclarationSyntax;

            // Add an additional overload if specified
            if (originalMethodNode == null ||
                (
                    !generationOptions.HasFlag(MethodCancellationToken.ForwardNone) &&
                    !generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone)
                ))
            {
                return(MethodTransformerResult.Update(methodNode));
            }
            var overloadNode = originalMethodNode
                               .ReturnAsTask(namespaceMetadata.TaskConflict)
                               .WithTriviaFrom(transformResult.Transformed)  // We want to have the sumamry of the transformed node but not the parameter list
                               .WithoutAnnotations(transformResult.Annotation)
                               .WithIdentifier(Identifier(methodNode.Identifier.ValueText));

            // We can have abstract methods that don't have a body
            if (methodResult.Symbol.IsAbstract)
            {
                // Add the trailing trivia from the semicolon to close paren
                overloadNode = overloadNode
                               .WithParameterList(
                    overloadNode.ParameterList
                    .WithCloseParenToken(
                        overloadNode.ParameterList.CloseParenToken.WithTrailingTrivia(overloadNode.SemicolonToken.TrailingTrivia))
                    )
                               .WithSemicolonToken(default(SyntaxToken));
                methodBody = Block()
                             .WithOpenBraceToken(
                    Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.OpenBraceToken,
                          TriviaList(transformResult.EndOfLineTrivia)))
                             .WithCloseBraceToken(
                    Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.CloseBraceToken,
                          TriviaList(transformResult.EndOfLineTrivia)));
            }

            var tokenArg = Argument(
                MemberAccessExpression(
                    SyntaxKind.SimpleMemberAccessExpression,
                    IdentifierName("CancellationToken"),
                    IdentifierName("None")));

            overloadNode = overloadNode
                           .WithBody(methodBody
                                     .WithStatements(
                                         SingletonList <StatementSyntax>(
                                             ReturnStatement(originalMethodNode.ForwardCall(methodResult.Symbol, methodNode.Identifier.ValueText, tokenArg))
                                             .WithReturnKeyword(
                                                 Token(TriviaList(transformResult.BodyLeadingWhitespaceTrivia), SyntaxKind.ReturnKeyword, TriviaList(Space))
                                                 )
                                             .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(transformResult.EndOfLineTrivia)))
                                             )
                                         ));
            if (generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone))
            {
                if (methodResult.Symbol.IsVirtual)
                {
                    // For virtual methods we need to remove the virtual keyword
                    overloadNode = overloadNode
                                   .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.VirtualKeyword))));
                }
                else if (methodResult.Symbol.OverriddenMethod != null)
                {
                    // For overrides we need to add the sealed keyword
                    overloadNode = overloadNode
                                   .WithModifiers(originalMethodNode.Modifiers.Add(Token(TriviaList(), SyntaxKind.SealedKeyword, TriviaList(Space))));
                }
                else if (methodResult.Symbol.IsAbstract)
                {
                    // For abstract we need to remove the abstract keyword
                    overloadNode = overloadNode
                                   .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.AbstractKeyword))));
                }
            }
            // We need to remove all directives
            while (overloadNode.ContainsDirectives)
            {
                overloadNode = overloadNode.RemoveNode(overloadNode.GetFirstDirective(), SyntaxRemoveOptions.KeepNoTrivia);
            }

            return(MethodTransformerResult.Update(methodNode)
                   .AddMethod(overloadNode));
        }
        /// <summary>
        /// The method with SplitTail needs to be splitted into two methods
        /// </summary>
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            var methodResult = transformResult.AnalyzationResult;

            if (!methodResult.SplitTail)
            {
                return(MethodTransformerResult.Skip);
            }

            var methodNode = transformResult.Transformed;
            // Tail method body shall contain all statements after preconditions
            var skipStatements = methodResult.Preconditions.Count;

            // If a cancellation guard was generated we need to skip also that
            if (_analyzeConfiguration.UseCancellationTokens && _analyzeConfiguration.CancellationTokens.Guards && methodResult.CancellationTokenRequired)
            {
                skipStatements++;
            }
            var tailMethodBody = methodNode.Body
                                 .WithStatements(new SyntaxList <StatementSyntax>()
                                                 .AddRange(methodNode.Body.Statements.Skip(skipStatements)));
            // Main method shall contain only preconditions and a call to the tail method
            var bodyStatements = new SyntaxList <StatementSyntax>()
                                 .AddRange(methodNode.Body.Statements.Take(skipStatements));
            ParameterListSyntax tailCallParameterList;
            // TODO: handle name collisions
            var tailIdentifier = Identifier("Internal" + methodNode.Identifier.Value);             // The transformed method has already the Async postfix
            MethodDeclarationSyntax      tailMethod   = null;
            LocalFunctionStatementSyntax tailFunction = null;

            if (_configuration.LocalFunctions)
            {
                tailFunction = LocalFunctionStatement(
                    methodNode.ReturnType.WithoutLeadingTrivia(),
                    tailIdentifier)
                               .WithParameterList(ParameterList()
                                                  .WithCloseParenToken(Token(TriviaList(), SyntaxKind.CloseParenToken, TriviaList(transformResult.EndOfLineTrivia))))
                               .AddAsync()
                               .WithLeadingTrivia(transformResult.LeadingWhitespaceTrivia)
                               .WithBody(tailMethodBody)
                               .AppendIndent(transformResult.IndentTrivia.ToFullString());
                // We do not need any parameter for the local function as we already have the parameters from the parent method
                tailCallParameterList = ParameterList();
            }
            else
            {
                var tailMethodModifiers = TokenList(
                    Token(TriviaList(transformResult.EndOfLineTrivia, transformResult.LeadingWhitespaceTrivia), SyntaxKind.PrivateKeyword, TriviaList(Space)));
                if (methodNode.Modifiers.Any(o => o.IsKind(SyntaxKind.StaticKeyword)))
                {
                    tailMethodModifiers = tailMethodModifiers.Add(Token(TriviaList(), SyntaxKind.StaticKeyword, TriviaList(Space)));
                }
                tailMethod = methodNode
                             .WithReturnType(methodNode.ReturnType.WithLeadingTrivia())            // Remove lead trivia in case the return type is the first node (eg. void Method())
                             .WithIdentifier(tailIdentifier)
                             .WithModifiers(tailMethodModifiers)
                             .AddAsync()
                             .WithBody(tailMethodBody);
                // Tail call shall contain the cancellation token parameter
                tailCallParameterList = methodNode.ParameterList;
            }

            var tailCall = ReturnStatement(
                Token(TriviaList(transformResult.BodyLeadingWhitespaceTrivia), SyntaxKind.ReturnKeyword, TriviaList(Space)),
                IdentifierName(tailIdentifier).Invoke(tailCallParameterList),
                Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(transformResult.EndOfLineTrivia))
                );

            bodyStatements = bodyStatements.Add(tailCall);
            // Append local function at the end for easier reading
            if (tailFunction != null)
            {
                bodyStatements = bodyStatements.Add(tailFunction);
            }
            methodNode = methodNode.WithBody(methodNode.Body
                                             .WithStatements(bodyStatements));

            var result = MethodTransformerResult.Update(methodNode);

            if (tailMethod != null)
            {
                result.AddMethod(tailMethod);
            }
            return(result);
        }
Exemplo n.º 11
0
 public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult methodTransformResult,
                                          ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
 {
     return(MethodTransformerResult.Update((MethodDeclarationSyntax)VisitMethodDeclaration(methodTransformResult.Transformed)));
 }
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult result,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            if (!result.TransformedLocks.Any() && !result.AnalyzationResult.MustRunSynchronized)
            {
                return(MethodTransformerResult.Skip);
            }
            var newFields = new Dictionary <string, FieldDeclarationSyntax>();
            var node      = result.Transformed;

            // Transform all lock statements that contains at least one async invocation.
            foreach (var lockResult in result.TransformedLocks)
            {
                var lockNode = node.GetAnnotatedNodes(lockResult.Annotation).OfType <LockStatementSyntax>().First();
                if (result.AnalyzationResult.FunctionReferences
                    .Where(r => lockResult.AnalyzationResult.Node.Span.Contains(r.ReferenceNameNode.Span))
                    .All(r => r.GetConversion() != ReferenceConversion.ToAsync))
                {
                    continue;
                }
                var lockSymbol    = lockResult.AnalyzationResult.Symbol;
                var typeLock      = lockSymbol is INamedTypeSymbol;
                var isStatic      = lockSymbol.IsStatic || typeLock;
                var lockFieldName = GetLockFieldName(lockResult.AnalyzationResult.Symbol, isStatic, typeLock ? "Lock" : "", "Async");
                // TODO: handle name collisions
                if (!typeMetadata.MemberNames.Contains(lockFieldName) && !newFields.ContainsKey(lockFieldName))
                {
                    newFields.Add(lockFieldName, GetAsyncLockField(lockFieldName, isStatic, result));
                }

                var usingNode = GetUsingAsyncLock(lockFieldName, lockNode.GetLeadingTrivia(),
                                                  lockNode.CloseParenToken.TrailingTrivia, lockNode.Statement);
                node = node.ReplaceNode(lockNode, usingNode);
            }

            if (result.AnalyzationResult.MustRunSynchronized)
            {
                var methodSymbol = result.AnalyzationResult.Symbol;
                var fieldName    = GetLockFieldName(methodSymbol, methodSymbol.IsStatic, typeMetadata);
                var newBody      = GetUsingAsyncLock(fieldName,
                                                     TriviaList(result.LeadingWhitespaceTrivia), TriviaList(result.EndOfLineTrivia), node.Body)
                                   .AppendIndent(result.IndentTrivia.ToFullString());
                node = node.WithBody(node.Body
                                     .WithStatements(SingletonList <StatementSyntax>(newBody)));

                // Remove the Synchronized option from the MethodImpl attribute
                var methodImplAttr    = node.AttributeLists.SelectMany(o => o.Attributes).First(o => o.Name.ToString() == "MethodImpl");
                var newMethodImplAttr = (AttributeSyntax)VisitAttribute(methodImplAttr);

                node = node.ReplaceNode(methodImplAttr, newMethodImplAttr);
                newFields.Add(fieldName, GetAsyncLockField(fieldName, methodSymbol.IsStatic, result));
            }

            var transformResult = MethodTransformerResult.Update(node);

            foreach (var newField in newFields.Values)
            {
                transformResult.AddField(newField);
            }
            return(transformResult);
        }
        private FieldDeclarationSyntax GetAsyncLockField(string fieldName, bool isStatic, IMethodOrAccessorTransformationResult result)
        {
            var list = TokenList(Token(TriviaList(result.LeadingWhitespaceTrivia), SyntaxKind.PrivateKeyword, TriviaList(Space)));

            if (isStatic)
            {
                list = list.Add(Token(TriviaList(), SyntaxKind.StaticKeyword, TriviaList(Space)));
            }
            list = list.Add(Token(TriviaList(), SyntaxKind.ReadOnlyKeyword, TriviaList(Space)));
            var lockType = SyntaxNodeExtensions.ConstructNameSyntax(_configuration.AsyncLockFullTypeName, Space);

            return(FieldDeclaration(
                       VariableDeclaration(lockType)
                       .WithVariables(
                           SingletonSeparatedList(
                               VariableDeclarator(
                                   Identifier(TriviaList(), fieldName, TriviaList(Space))
                                   )
                               .WithInitializer(
                                   EqualsValueClause(
                                       ObjectCreationExpression(SyntaxNodeExtensions.ConstructNameSyntax(_configuration.AsyncLockFullTypeName))
                                       .WithArgumentList(ArgumentList())
                                       .WithNewKeyword(Token(TriviaList(), SyntaxKind.NewKeyword, TriviaList(Space)))
                                       )
                                   .WithEqualsToken(Token(TriviaList(), SyntaxKind.EqualsToken, TriviaList(Space)))
                                   )
                               )
                           )
                       )
                   .WithModifiers(list)
                   .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(result.EndOfLineTrivia))));
        }
        // TODO: should we always add guards in anonymous methods?
        //public SyntaxNode Transform(IFunctionTransformationResult functionTransformResult, ITypeTransformationMetadata typeMetadata,
        //	INamespaceTransformationMetadata namespaceMetadata)
        //{
        //	var methodResult = functionTransformResult.AnalyzationResult.GetMethodOrAccessor();
        //	if (methodResult == null || !methodResult.CancellationTokenRequired)
        //	{
        //		return null;
        //	}

        //	var functionNode = functionTransformResult.Transformed;
        //	var functionBody = functionNode.GetFunctionBody() as BlockSyntax; // TODO: support expressions
        //	if (functionBody != null)
        //	{
        //		functionNode = functionNode.ReplaceNode(functionBody,
        //			AddGuards(functionTransformResult, functionBody, functionTransformResult.AnalyzationResult, CancellationTokenParamName));
        //	}

        //	return functionNode;
        //}

        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            var methodResult = transformResult.AnalyzationResult;

            if (!methodResult.CancellationTokenRequired)
            {
                return(MethodTransformerResult.Skip);
            }
            var originalNode      = methodResult.GetNode();
            var generationOptions = methodResult.MethodCancellationToken.GetValueOrDefault();
            var methodNode        = transformResult.Transformed;

            methodNode = methodNode
                         .AddCancellationTokenParameter(CancellationTokenParamName,
                                                        generationOptions.HasFlag(MethodCancellationToken.Optional),
                                                        transformResult.LeadingWhitespaceTrivia,
                                                        transformResult.EndOfLineTrivia);

            var methodBody = methodNode.Body;

            methodNode = methodNode.WithBody(AddGuards(transformResult, methodBody, methodResult, CancellationTokenParamName));

            var originalMethodNode = originalNode as MethodDeclarationSyntax;

            // Add an additional overload if specified
            if (originalMethodNode == null ||
                (
                    !generationOptions.HasFlag(MethodCancellationToken.ForwardNone) &&
                    !generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone)
                ))
            {
                return(MethodTransformerResult.Update(methodNode));
            }
            var overloadNode = originalMethodNode
                               .ReturnAsTask(namespaceMetadata.TaskConflict)
                               .WithTriviaFrom(transformResult.Transformed)  // We want to have the sumamry of the transformed node but not the parameter list
                               .WithoutAnnotations(transformResult.Annotation)
                               .WithIdentifier(Identifier(methodNode.Identifier.ValueText));

            // We can have abstract methods that don't have a body
            if (methodResult.Symbol.IsAbstract)
            {
                // Add the trailing trivia from the semicolon to close paren
                overloadNode = overloadNode
                               .WithParameterList(
                    overloadNode.ParameterList
                    .WithCloseParenToken(
                        overloadNode.ParameterList.CloseParenToken.WithTrailingTrivia(overloadNode.SemicolonToken.TrailingTrivia))
                    )
                               .WithSemicolonToken(default(SyntaxToken));
                methodBody = Block()
                             .WithOpenBraceToken(
                    Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.OpenBraceToken,
                          TriviaList(transformResult.EndOfLineTrivia)))
                             .WithCloseBraceToken(
                    Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.CloseBraceToken,
                          TriviaList(transformResult.EndOfLineTrivia)));
            }

            var tokenArg = Argument(
                MemberAccessExpression(
                    SyntaxKind.SimpleMemberAccessExpression,
                    IdentifierName("CancellationToken"),
                    IdentifierName("None")));

            overloadNode = overloadNode
                           .WithBody(methodBody
                                     .WithStatements(
                                         SingletonList <StatementSyntax>(
                                             ReturnStatement(originalMethodNode.ForwardCall(methodResult.Symbol, methodNode.Identifier.ValueText, tokenArg))
                                             .WithReturnKeyword(
                                                 Token(TriviaList(transformResult.BodyLeadingWhitespaceTrivia), SyntaxKind.ReturnKeyword, TriviaList(Space))
                                                 )
                                             .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(transformResult.EndOfLineTrivia)))
                                             )
                                         ));
            if (generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone))
            {
                if (methodResult.Symbol.IsVirtual)
                {
                    // For virtual methods we need to remove the virtual keyword
                    overloadNode = overloadNode
                                   .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.VirtualKeyword))));
                }
                else if (methodResult.Symbol.OverriddenMethod != null)
                {
                    // For overrides we need to add the sealed keyword
                    overloadNode = overloadNode
                                   .WithModifiers(originalMethodNode.Modifiers.Add(Token(TriviaList(), SyntaxKind.SealedKeyword, TriviaList(Space))));
                }
                else if (methodResult.Symbol.IsAbstract)
                {
                    // For abstract we need to remove the abstract keyword
                    overloadNode = overloadNode
                                   .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.AbstractKeyword))));
                }
            }
            // We need to remove all directives
            while (overloadNode.ContainsDirectives)
            {
                overloadNode = overloadNode.RemoveNode(overloadNode.GetFirstDirective(), SyntaxRemoveOptions.KeepNoTrivia);
            }

            return(MethodTransformerResult.Update(methodNode)
                   .AddMethod(overloadNode));
        }
        public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult,
                                                 ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata)
        {
            var methodResult = transformResult.AnalyzationResult;

            if (!methodResult.Missing || !methodResult.Conversion.HasFlag(MethodConversion.ToAsync) || methodResult.Symbol.IsObsolete())
            {
                return(MethodTransformerResult.Skip);
            }
            var methodNode = transformResult.Transformed;

            var baseMethod = methodResult.RelatedMethods
                             .Where(o =>
                                    (methodResult.BaseOverriddenMethod != null && o.Symbol.Equals(methodResult.BaseOverriddenMethod)) ||
                                    methodResult.ImplementedInterfaces.Any(i => o.Symbol.Equals(i)))
                             .FirstOrDefault(o => o.AsyncCounterpartSymbol?.IsObsolete() == true);

            if (baseMethod == null)
            {
                return(MethodTransformerResult.Skip);
            }

            namespaceMetadata.AddUsing("System");
            AttributeListSyntax obsoleteAttribute = null;
            var          syntaxReference          = baseMethod.AsyncCounterpartSymbol.DeclaringSyntaxReferences.SingleOrDefault();
            SyntaxTrivia?documentationTrivia      = null;

            if (syntaxReference != null)
            {
                var baseMethodNode = syntaxReference.GetSyntax() as MethodDeclarationSyntax;
                obsoleteAttribute   = baseMethodNode?.AttributeLists.FirstOrDefault(o => o.Attributes.Count == 1 && o.Attributes.First().Name.ToString() == "Obsolete");
                obsoleteAttribute   = (AttributeListSyntax)_directiveRemover.VisitAttributeList(obsoleteAttribute);
                documentationTrivia = obsoleteAttribute.GetLeadingTrivia()
                                      .Select(o => o.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) ? o : (SyntaxTrivia?)null)
                                      .FirstOrDefault(o => o.HasValue);
            }

            if (obsoleteAttribute == null)
            {
                obsoleteAttribute = AttributeList(SingletonSeparatedList(Attribute(IdentifierName("Obsolete"))))
                                    .WithOpenBracketToken(Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.OpenBracketToken, TriviaList()))
                                    .WithCloseBracketToken(Token(TriviaList(), SyntaxKind.CloseBracketToken, TriviaList(transformResult.EndOfLineTrivia)));
            }

            var inheritDocTrivia = Trivia(GetInheritdoc(transformResult.EndOfLineTrivia.ToFullString()));

            if (documentationTrivia.HasValue)
            {
                obsoleteAttribute = obsoleteAttribute.WithLeadingTrivia(obsoleteAttribute.GetLeadingTrivia()
                                                                        .Replace(documentationTrivia.Value, inheritDocTrivia));
            }
            else
            {
                // Append <inheritdoc />
                var leadingTrivia = obsoleteAttribute.GetLeadingTrivia();
                var trivias       = new List <SyntaxTrivia>();
                if (leadingTrivia.Count == 0 || !leadingTrivia.Last().IsKind(SyntaxKind.WhitespaceTrivia))
                {
                    trivias.Add(transformResult.LeadingWhitespaceTrivia);
                }

                trivias.Add(inheritDocTrivia);
                trivias.Add(transformResult.LeadingWhitespaceTrivia);
                obsoleteAttribute = obsoleteAttribute.WithLeadingTrivia(leadingTrivia.AddRange(trivias));
            }

            methodNode = methodNode
                         .WithLeadingTrivia(TriviaList(transformResult.LeadingWhitespaceTrivia))
                         .WithAttributeLists(methodNode.AttributeLists.Add(obsoleteAttribute));

            return(MethodTransformerResult.Update(methodNode));
        }