public SyntaxNode TransformFunctionReference(SyntaxNode node, IFunctionAnalyzationResult funcResult,
                                              IFunctionReferenceAnalyzationResult funcReferenceResult, INamespaceTransformationMetadata namespaceMetadata)
 {
     if (funcReferenceResult is IBodyFunctionReferenceAnalyzationResult bodyFunctionReference &&
         (bodyFunctionReference.PassCancellationToken || funcResult.GetMethodOrAccessor().AddCancellationTokenGuards))
     {
         // Mark the invocation node in order to add the OperationCanceledException catch block only if there is at least one async invocation
         // with a cancellation token passed as an argument or there will be a cancellation token guard added
         return(node.WithAdditionalAnnotations(new SyntaxAnnotation(Annotations.AsyncCallWithTokenOrGuard)));
     }
     return(null);
 }
Ejemplo n.º 2
0
 private InvocationExpressionSyntax UpdateTypeAndRunReferenceTransformers(InvocationExpressionSyntax node, IFunctionAnalyzationResult funcResult,
                                                                          IFunctionReferenceAnalyzationResult funcReferenceResult,
                                                                          INamespaceTransformationMetadata namespaceMetadata,
                                                                          Func <MemberAccessExpressionSyntax, INamedTypeSymbol, bool, MemberAccessExpressionSyntax> updateTypeFunc)
 {
     // If the async counterpart is from another type e.g. Thread.Sleep -> Task.Delay, we need to change also the type
     if (!funcReferenceResult.AsyncCounterpartSymbol.IsExtensionMethod &&
         !funcReferenceResult.AsyncCounterpartSymbol.OriginalDefinition.ContainingType.Equals(
             funcReferenceResult.ReferenceSymbol.OriginalDefinition.ContainingType) &&
         node.Expression is MemberAccessExpressionSyntax memberAccess)
     {
         var type = funcReferenceResult.AsyncCounterpartSymbol.ContainingType;
         node = node.WithExpression(updateTypeFunc(memberAccess, type,
                                                   namespaceMetadata.AnalyzationResult.IsIncluded(type.ContainingNamespace?.ToString())));
     }
     return(RunReferenceTransformers(node, funcResult, funcReferenceResult, namespaceMetadata));
 }
Ejemplo n.º 3
0
 private T RunReferenceTransformers <T>(T node, IFunctionAnalyzationResult funcResult, IFunctionReferenceAnalyzationResult funcReferenceResult,
                                        INamespaceTransformationMetadata namespaceMetadata)
     where T : SyntaxNode
 {
     foreach (var transformer in _configuration.FunctionReferenceTransformers)
     {
         node = (T)transformer.TransformFunctionReference(node, funcResult, funcReferenceResult, namespaceMetadata) ?? node;
     }
     return(node);
 }
Ejemplo n.º 4
0
 private T UpdateTypeAndRunReferenceTransformers <T>(T node, IFunctionAnalyzationResult funcResult, IFunctionReferenceAnalyzationResult funcReferenceResult,
                                                     INamespaceTransformationMetadata namespaceMetadata, Func <INamedTypeSymbol, bool, T> updateTypeFunc)
     where T : SyntaxNode
 {
     // If the async counterpart is from another type e.g. Thread.Sleep -> Task.Delay, we need to change also the type
     if (!funcReferenceResult.AsyncCounterpartSymbol.IsExtensionMethod &&
         !funcReferenceResult.AsyncCounterpartSymbol.OriginalDefinition.ContainingType.Equals(
             funcReferenceResult.ReferenceSymbol.OriginalDefinition.ContainingType))
     {
         var type = funcReferenceResult.AsyncCounterpartSymbol.ContainingType;
         node = updateTypeFunc(type, namespaceMetadata.AnalyzationResult.IsIncluded(type.ContainingNamespace?.ToString()));
     }
     return(RunReferenceTransformers(node, funcResult, funcReferenceResult, namespaceMetadata));
 }
        private T TransformConditionalAccessToConditionalExpressions <T>(
            T node,
            SimpleNameSyntax nameNode,
            IFunctionReferenceAnalyzationResult funReferenceResult,
            ITypeTransformationMetadata typeMetadata,
            ConditionalAccessExpressionSyntax conditionalAccessNode,
            InvocationExpressionSyntax invokeNode) where T : SyntaxNode
        {
            // TODO: we should check the async symbol instead
            var returnType  = funReferenceResult.ReferenceSymbol.ReturnType;
            var type        = returnType.CreateTypeSyntax();
            var canSkipCast = !returnType.IsValueType && !returnType.IsNullable();

            if (returnType.IsValueType && !returnType.IsNullable())
            {
                type = NullableType(type);
            }

            ExpressionSyntax whenNotNullNode = null;

            if (invokeNode.Parent is MemberAccessExpressionSyntax memberAccessParent)
            {
                whenNotNullNode = conditionalAccessNode.WhenNotNull
                                  .ReplaceNode(memberAccessParent,
                                               MemberBindingExpression(Token(SyntaxKind.DotToken), memberAccessParent.Name));
            }
            else if (invokeNode.Parent is ElementAccessExpressionSyntax elementAccessParent)
            {
                whenNotNullNode = conditionalAccessNode.WhenNotNull
                                  .ReplaceNode(elementAccessParent,
                                               ElementBindingExpression(elementAccessParent.ArgumentList));
            }

            var             valueNode         = conditionalAccessNode.Expression.WithoutTrivia();
            StatementSyntax variableStatement = null;
            BlockSyntax     statementBlock    = null;
            var             statementIndex    = 0;

            // We have to save the value in a variable when the expression is an invocation, index accessor or property in
            // order to prevent double calls
            // TODO: find a more robust solution
            if (!(conditionalAccessNode.Expression is SimpleNameSyntax simpleName) || char.IsUpper(simpleName.ToString()[0]))
            {
                var statement = (StatementSyntax)conditionalAccessNode.Ancestors().FirstOrDefault(o => o is StatementSyntax);
                if (statement == null || !(statement.Parent is BlockSyntax block))
                {
                    // TODO: convert arrow method/property/function to a normal one
                    // TODO: convert to block if there is no block
                    throw new NotSupportedException(
                              $"Arrow method with null-conditional is not supported. Node: {conditionalAccessNode}");
                }

                var leadingTrivia = statement.GetLeadingTrivia();
                var fnName        = nameNode.Identifier.ValueText;
                statementIndex = block.Statements.IndexOf(statement);
                statementBlock = block;
                // TODO: handle name collisions
                var variableName = $"{char.ToLowerInvariant(fnName[0])}{fnName.Substring(1)}{statementIndex}";
                variableStatement = LocalDeclarationStatement(
                    VariableDeclaration(
                        IdentifierName(Identifier(leadingTrivia, "var", TriviaList(Space))),
                        SingletonSeparatedList(
                            VariableDeclarator(
                                Identifier(TriviaList(), variableName, TriviaList(Space)))
                            .WithInitializer(
                                EqualsValueClause(valueNode)
                                .WithEqualsToken(Token(TriviaList(), SyntaxKind.EqualsToken, TriviaList(Space)))
                                )
                            )))
                                    .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(typeMetadata.EndOfLineTrivia)));

                valueNode = IdentifierName(variableName);
            }

            var invocationAnnotation = Guid.NewGuid().ToString();
            var nullNode             = LiteralExpression(
                SyntaxKind.NullLiteralExpression,
                Token(TriviaList(), SyntaxKind.NullKeyword, TriviaList(Space)));
            var ifNullCondition = BinaryExpression(
                SyntaxKind.EqualsExpression,
                valueNode.WithTrailingTrivia(TriviaList(Space)),
                nullNode)
                                  .WithOperatorToken(
                Token(TriviaList(), SyntaxKind.EqualsEqualsToken, TriviaList(Space)));

            ExpressionSyntax wrappedNode = ParenthesizedExpression(
                ConditionalExpression(
                    ifNullCondition,
                    canSkipCast
                                                        ? (ExpressionSyntax)nullNode
                                                        : CastExpression(
                        Token(SyntaxKind.OpenParenToken),
                        type,
                        Token(TriviaList(), SyntaxKind.CloseParenToken, TriviaList(Space)),
                        nullNode),
                    InvocationExpression(
                        MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
                                               valueNode,
                                               ((MemberBindingExpressionSyntax)invokeNode.Expression).Name))
                    .WithAdditionalAnnotations(new SyntaxAnnotation(invocationAnnotation))
                    .WithArgumentList(invokeNode.ArgumentList.WithoutTrailingTrivia())
                    )
                .WithColonToken(Token(TriviaList(), SyntaxKind.ColonToken, TriviaList(Space)))
                .WithQuestionToken(Token(TriviaList(), SyntaxKind.QuestionToken, TriviaList(Space)))
                );

            if (whenNotNullNode != null)
            {
                wrappedNode = conditionalAccessNode
                              .WithExpression(wrappedNode)
                              .WithWhenNotNull(whenNotNullNode);
            }

            wrappedNode = wrappedNode.WithTriviaFrom(conditionalAccessNode);
            invokeNode  = (InvocationExpressionSyntax)wrappedNode.GetAnnotatedNodes(invocationAnnotation).First();
            wrappedNode = wrappedNode.ReplaceNode(invokeNode, invokeNode.AddAwait(_configuration.ConfigureAwaitArgument));
            if (statementBlock != null)
            {
                var newBlock = statementBlock.ReplaceNode(conditionalAccessNode, wrappedNode);
                newBlock = newBlock.WithStatements(newBlock.Statements.Insert(statementIndex, variableStatement));

                return(node.ReplaceNode(statementBlock, newBlock));
            }

            return(node.ReplaceNode(conditionalAccessNode, wrappedNode));
        }
Ejemplo n.º 6
0
 public FunctionReferenceTransformationResult(IFunctionReferenceAnalyzationResult analyzationResult) : base(analyzationResult.ReferenceNameNode)
 {
     AnalyzationResult = analyzationResult;
 }
Ejemplo n.º 7
0
        public SyntaxNode TransformFunctionReference(SyntaxNode node, IFunctionAnalyzationResult funcResult,
                                                     IFunctionReferenceAnalyzationResult funcReferenceResult,
                                                     INamespaceTransformationMetadata namespaceMetadata)
        {
            if (!funcReferenceResult.AsyncCounterpartSymbol.Equals(_whenAllMethod))
            {
                return(node);
            }
            if (!(node is InvocationExpressionSyntax invokeNode) ||
                !(funcReferenceResult is IBodyFunctionReferenceAnalyzationResult bodyReference))
            {
                return(node);                // Cref
            }

            // Here are some examples of expected nodes
            // Task.WhenAll(Results, ReadAsync)
            // Task.WhenAll(1, 100, ReadAsync)
            // Task.WhenAll(Enumerable.Empty<string>(), ReadAsync)
            // Task.WhenAll(GetStringList(), i =>
            // {
            //	return SimpleFile.ReadAsync();
            // })

            // For Parallel.ForEach, we need to combine the two arguments into one, using the Select Linq extension e.g.
            // Task.WhenAll(Results.Select(i => ReadAsync(i))
            // For Parallel.For, we need to move the first two parameters into Enumerable.Range and then apply the same logic as for
            // Parallel.ForEach

            var actionParam  = bodyReference.ReferenceSymbol.Parameters.Last();
            var actionType   = actionParam.Type as INamedTypeSymbol;
            var actionMethod = actionType?.DelegateInvokeMethod;

            if (actionMethod == null)
            {
                throw new InvalidOperationException(
                          $"Unable to transform Parallel.{bodyReference.ReferenceSymbol.Name} to Task.WaitAll. " +
                          $"The second Parallel.{bodyReference.ReferenceSymbol.Name} argument is not a delegate, but is {actionParam.Type}");
            }
            namespaceMetadata.AddUsing("System.Linq");
            var newExpression = invokeNode.ArgumentList.Arguments.Last().Expression;

            if (!(newExpression is AnonymousFunctionExpressionSyntax))
            {
                var delArgument = bodyReference.DelegateArguments.Last();
                var cancellationTokenParamName = funcResult.GetMethodOrAccessor().CancellationTokenRequired
                                        ? "cancellationToken"
                                        : null; // TODO: find a way to not have this duplicated and fix naming colision
                newExpression = newExpression.WrapInsideFunction(actionMethod, false, namespaceMetadata.TaskConflict,
                                                                 invoke => invoke.AddCancellationTokenArgumentIf(cancellationTokenParamName, delArgument.BodyFunctionReference));
            }
            ExpressionSyntax enumerableExpression;

            if (bodyReference.ReferenceSymbol.Equals(_forMethod))
            {
                // Construct an Enumerable.Range(1, 10 - 1), where 1 and 10 are the first two arguments of Parallel.For method
                var startArg = invokeNode.ArgumentList.Arguments.First();
                enumerableExpression = InvocationExpression(
                    MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
                                           IdentifierName("Enumerable").WithLeadingTrivia(startArg.GetLeadingTrivia()),
                                           Token(SyntaxKind.DotToken),
                                           IdentifierName("Range")),
                    ArgumentList(
                        SeparatedList <ArgumentSyntax>(
                            new SyntaxNodeOrToken[]
                {
                    startArg.WithoutTrivia(),
                    Token(TriviaList(), SyntaxKind.CommaToken, TriviaList(Space)),
                    Argument(
                        BinaryExpression(SyntaxKind.SubtractExpression,
                                         invokeNode.ArgumentList.Arguments.Skip(1).First().Expression.WithoutTrivia().WithTrailingTrivia(Space),
                                         Token(TriviaList(), SyntaxKind.MinusToken, TriviaList(Space)),
                                         startArg.WithoutTrivia().Expression))
                }
                            )
                        )
                    );
            }
            else
            {
                enumerableExpression = invokeNode.ArgumentList.Arguments.First().Expression;                 // For ForEach take the first parmeter e.g. Enumerable.Range(1, 10)
            }

            var memberAccess = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
                                                      enumerableExpression,
                                                      Token(SyntaxKind.DotToken),
                                                      IdentifierName("Select"));
            var argument = InvocationExpression(memberAccess)
                           .WithArgumentList(
                ArgumentList(SingletonSeparatedList(Argument(newExpression.WithoutTrivia())))
                .WithCloseParenToken(Token(TriviaList(), SyntaxKind.CloseParenToken, newExpression.GetTrailingTrivia()))
                );

            return(invokeNode.WithArgumentList(
                       invokeNode.ArgumentList.WithArguments(SingletonSeparatedList(Argument(argument)))));
        }