コード例 #1
0
        public async Task <Document> GenerateProxyAsync(Workspace workspace, Project project,
                                                        CancellationToken cancellationToken, params INamedTypeSymbol[] types)
        {
            // TODO: the project *must* have a reference to the Moq.Proxy assembly. How do we verify that?

            // Sort interfaces so regardless of order, we reuse the proxies
            if (types.FirstOrDefault()?.TypeKind == TypeKind.Interface)
            {
                Array.Sort(types, Comparer <INamedTypeSymbol> .Create((x, y) => x.Name.CompareTo(y.Name)));
            }
            else if (types.Length > 1)
            {
                Array.Sort(types, 1, types.Length - 1, Comparer <INamedTypeSymbol> .Create((x, y) => x.Name.CompareTo(y.Name)));
            }

            var generator = SyntaxGenerator.GetGenerator(project);
            var name      = "ProxyOf" + string.Join("", types.Select(x => x.Name));

            var syntax = generator.CompilationUnit(types
                                                   .Where(x => x.ContainingNamespace != null)
                                                   .Select(x => x.ContainingNamespace.ToDisplayString())
                                                   .Concat(new[]
            {
                typeof(EventArgs).Namespace,
                typeof(IList <>).Namespace,
                typeof(MethodBase).Namespace,
                typeof(IProxy).Namespace,
            })
                                                   .Distinct()
                                                   .Select(x => generator.NamespaceImportDeclaration(x))
                                                   .Concat(new[]
            {
                generator.ClassDeclaration(name,
                                           accessibility: Accessibility.Public,
                                           interfaceTypes: types.Select(x => generator.IdentifierName(x.Name))
                                           // NOTE: we *always* append IProxy at the end, which is what we use
                                           // in ImplementInterfaces to determine that it must *always* be implemented
                                           // explicitly.
                                           .Concat(new[] { generator.IdentifierName(nameof(IProxy)) }))
            }));

            var languageServices = workspace.Services.GetService <LanguageServiceRetriever>();

            var implementInterfaceService = languageServices.GetLanguageServices(project.Language,
                                                                                 "Microsoft.CodeAnalysis.ImplementInterface.IImplementInterfaceService").FirstOrDefault();

            if (implementInterfaceService == null)
            {
                // TODO: improve
                throw new NotSupportedException(project.Language);
            }

            var            getCodeActionsMethod = implementInterfaceService.GetType().GetMethod("GetCodeActions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            GetCodeActions getCodeActions       = (d, s, n, t) => ((IEnumerable <CodeAction>)getCodeActionsMethod.Invoke(
                                                                       implementInterfaceService,
                                                                       new object[] { d, s, n, t }));

            var document = project.AddDocument(name, syntax);

            document = await ImplementInterfaces(workspace, document, generator, getCodeActions, cancellationToken);

            var rewriters = languageServices.GetLanguageServices <IDocumentRewriter>(project.Language).ToArray();

            foreach (var rewriter in rewriters)
            {
                document = await rewriter.VisitAsync(document, cancellationToken);
            }

            return(document);
        }
コード例 #2
0
        async Task <Document> ImplementInterfaces(Workspace workspace, Document document, SyntaxGenerator generator, GetCodeActions getCodeActions, CancellationToken cancellationToken)
        {
            var project = document.Project;

            var compilation = await project.GetCompilationAsync(cancellationToken);

            var tree = await document.GetSyntaxTreeAsync(cancellationToken);

            var semantic = await document.GetSemanticModelAsync(cancellationToken);

            var syntax    = tree.GetRoot().DescendantNodes().First(node => generator.GetDeclarationKind(node) == DeclarationKind.Class);
            var baseTypes = generator.GetBaseAndInterfaceTypes(syntax);

            var actions = baseTypes.Select(baseType => new
            {
                BaseType    = baseType,
                CodeActions = getCodeActions(document, semantic, baseType, cancellationToken)
#if DEBUG
                              .ToArray()
#endif
            })
                          .Where(x => x.CodeActions.Any())
#if DEBUG
                          .ToArray()
#endif
            ;

            // We're done.
            if (!actions.Any())
            {
                return(document);
            }

            var action  = default(CodeAction);
            var current = actions.First();

            // The last base type is IProxy, we implement that explicitly always.
            // NOTE: VB doesn't have this concept, it always adds Implements [member].
            // So there is always a single CodeAction for it. The VisualBasicProxyRewriter takes
            // care of making the property private, which is how you make it "explicit" in VB.
            if (current.BaseType == baseTypes.Last())
            {
                action = current.CodeActions.Last();
            }
            else
            {
                action = current.CodeActions.First();
            }

            // Otherwise, apply and recurse.
            var operations = await action.GetOperationsAsync(cancellationToken);

            var operation = operations.OfType <ApplyChangesOperation>().FirstOrDefault();

            if (operation != null)
            {
                operation.Apply(workspace, cancellationToken);
                return(await ImplementInterfaces(workspace, operation.ChangedSolution.GetDocument(document.Id), generator, getCodeActions, cancellationToken));
            }

            return(document);
        }