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)); }