示例#1
0
            protected override async Task <Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
            {
                var generator = SyntaxGenerator.GetGenerator(document);
                var root      = await setup.SyntaxTree.GetRootAsync(cancellationToken);

                var node      = FindSetup(root);
                var member    = setup.Ancestors().First(x => generator.GetDeclarationKind(x) == DeclarationKind.Method);
                var @class    = member.Ancestors().First(x => generator.GetDeclarationKind(x) == DeclarationKind.Class);
                var @delegate = generator.GetMembers(@class)
                                // Just check by name for now. Might need to also ensure signature compatibility.
                                .FirstOrDefault(x => generator.GetName(x) == symbol.Name);

                var delegateName = symbol.Name;
                var signature    = generator.DelegateDeclaration(
                    delegateName,
                    parameters: symbol.Parameters.Select(prm => generator.ParameterDeclaration(prm)),
                    returnType: symbol.ReturnsVoid ? null : generator.TypeExpression(symbol.ReturnType));

                if (@delegate == null)
                {
                    root = root.InsertNodesAfter(member, new[] { signature });
                    // Find the updated setup
                    node = FindSetup(root);
                }
                else
                {
                    var tempDoc = document.WithSyntaxRoot(generator.ReplaceNode(root, @delegate, signature));
                    tempDoc = await Simplifier.ReduceAsync(tempDoc);

                    var tempRoot = await tempDoc.GetSyntaxRootAsync(cancellationToken);

                    var className = generator.GetName(@class);
                    var tempClass = tempRoot.DescendantNodes().First(x =>
                                                                     generator.GetDeclarationKind(x) == DeclarationKind.Class &&
                                                                     generator.GetName(x) == className);
                    var tempDelegate = generator.GetMembers(tempClass)
                                       .First(x => generator.GetName(x) == delegateName);

                    if ([email protected](tempDelegate, true))
                    {
                        // Generate the delegate name using full Type+Member name.
                        var semantic = await document.GetSemanticModelAsync(cancellationToken);

                        var mock = semantic.GetSymbolInfo(
                            setup.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.InvocationExpression) ?
                            (SyntaxNode)((setup as CS.InvocationExpressionSyntax)?.Expression as CS.MemberAccessExpressionSyntax)?.Expression :
                            ((setup as VB.InvocationExpressionSyntax)?.Expression as VB.MemberAccessExpressionSyntax)?.Expression);

                        if (mock.Symbol != null &&
                            mock.Symbol.Kind == SymbolKind.Local ||
                            mock.Symbol.Kind == SymbolKind.Field)
                        {
                            var type = mock.Symbol.Kind == SymbolKind.Local ?
                                       ((ILocalSymbol)mock.Symbol).Type :
                                       ((IFieldSymbol)mock.Symbol).Type;

                            delegateName = type.MetadataName + symbol.Name;
                            signature    = generator.WithName(signature, delegateName);
                            root         = root.InsertNodesAfter(member, new[] { signature });
                            // Find the updated setup
                            node = FindSetup(root);
                        }
                    }
                }

                root = generator.ReplaceNode(root, node, generator.WithTypeArguments(node, generator.IdentifierName(delegateName)));
                // Find the updated setup
                node = FindSetup(root);

                // Detect recursive mock access and wrap in a Func<TDelegate>
                if (
                    node.Parent.ChildNodes()
                    .OfType <CS.ArgumentListSyntax>()
                    .Where(list => !list.Arguments.Select(arg => arg.Expression).OfType <CS.LambdaExpressionSyntax>().Any())
                    .SelectMany(list => list.DescendantNodes().OfType <CS.MemberAccessExpressionSyntax>())
                    .Count() > 1 ||
                    node.Parent.ChildNodes()
                    .OfType <VB.ArgumentListSyntax>()
                    .Where(list => !list.Arguments.Select(arg => arg.GetExpression()).OfType <VB.LambdaExpressionSyntax>().Any())
                    .SelectMany(list => list.DescendantNodes().OfType <VB.MemberAccessExpressionSyntax>())
                    .Count() > 1)
                {
                    var expression = node.Parent.ChildNodes().Last()
                                     .DescendantNodes()
                                     .Where(x =>
                                            x.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SimpleMemberAccessExpression) ||
                                            // For VB, we actually wrap the AddressOf
                                            x.IsKind(Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.AddressOfExpression))
                                     .First();

                    root = generator.ReplaceNode(root, expression,
                                                 generator.ValueReturningLambdaExpression(expression));
                    // Find the updated setup
                    node = FindSetup(root);
                }

                // If there is no Returns, generate one
                if (node.Parent.Parent.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionStatement))
                {
                    var returns = generator.InvocationExpression(
                        generator.MemberAccessExpression(
                            node.Parent.WithTrailingTrivia(
                                node.Parent.Parent.GetLeadingTrivia().Add(CSFactory.Whitespace("\t"))),
                            "Returns"),
                        generator.ValueReturningLambdaExpression(
                            symbol.Parameters.Select(prm => generator.ParameterDeclaration(prm)),
                            generator.ThrowExpression(generator.NullLiteralExpression())));

                    // Replace the parent InvocationExpression with the returning one.
                    root = generator.ReplaceNode(root, node.Parent, returns);
                }
                else if (node.Parent.Parent.IsKind(Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.ExpressionStatement))
                {
                    var lambda = VBFactory.MultiLineFunctionLambdaExpression(
                        VBFactory.FunctionLambdaHeader().WithParameterList(
                            VBFactory.ParameterList(
                                VBFactory.SeparatedList(
                                    symbol.Parameters.Select(prm => (VB.ParameterSyntax)generator.ParameterDeclaration(prm))))),
                        VBFactory.List(new VB.StatementSyntax[]
                    {
                        VBFactory.ThrowStatement(
                            VBFactory.ObjectCreationExpression(
                                VBFactory.ParseTypeName(nameof(NotImplementedException))))
                    }),
                        VBFactory.EndFunctionStatement());

                    var returns = generator.InvocationExpression(
                        generator.MemberAccessExpression(
                            node.Parent.WithTrailingTrivia(
                                node.Parent.Parent.GetLeadingTrivia()
                                .Insert(0, VBFactory.Whitespace(" "))
                                .Insert(1, VBFactory.LineContinuationTrivia(""))
                                .Add(VBFactory.Whitespace("\t"))),
                            "Returns"),
                        lambda);

                    // Replace the parent InvocationExpression with the returning one.
                    root = generator.ReplaceNode(root, node.Parent, returns);
                }

                return(document.WithSyntaxRoot(root));
            }