".Replace("\r\n", "\n").Replace("\n", Environment.NewLine); // normalize regardless of git checkout policy

        /// <summary>
        /// Produces a new document in response to any code generation attributes found in the specified document.
        /// </summary>
        /// <param name="compilation">The compilation to which the document belongs.</param>
        /// <param name="inputDocument">The document to scan for generator attributes.</param>
        /// <param name="projectDirectory">The path of the <c>.csproj</c> project file.</param>
        /// <param name="assemblyLoader">A function that can load an assembly with the given name.</param>
        /// <param name="progress">Reports warnings and errors in code generation.</param>
        /// <returns>A task whose result is the generated document.</returns>
        public static async Task <SyntaxTree> TransformAsync(
            CSharpCompilation compilation,
            SyntaxTree inputDocument,
            string projectDirectory,
            Func <AssemblyName, Assembly> assemblyLoader,
            IProgress <Diagnostic> progress)
        {
            Requires.NotNull(compilation, nameof(compilation));
            Requires.NotNull(inputDocument, nameof(inputDocument));
            Requires.NotNull(assemblyLoader, nameof(assemblyLoader));

            var inputSemanticModel   = compilation.GetSemanticModel(inputDocument);
            var inputCompilationUnit = inputDocument.GetCompilationUnitRoot();

            var emittedExterns = inputCompilationUnit
                                 .Externs
                                 .Select(x => x.WithoutTrivia())
                                 .ToImmutableArray();

            var emittedUsings = inputCompilationUnit
                                .Usings
                                .Select(x => x.WithoutTrivia())
                                .ToImmutableArray();

            var emittedAttributeLists = ImmutableArray <AttributeListSyntax> .Empty;
            var emittedMembers        = ImmutableArray <MemberDeclarationSyntax> .Empty;

            var memberNodes = inputDocument
                              .GetRoot()
                              .DescendantNodesAndSelf(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax || n is TypeDeclarationSyntax)
                              .OfType <CSharpSyntaxNode>();

            foreach (var memberNode in memberNodes)
            {
                var attributeData = GetAttributeData(compilation, inputSemanticModel, memberNode);
                var generators    = FindCodeGenerators(attributeData, assemblyLoader);
                foreach (var generator in generators)
                {
                    var context = new TransformationContext(memberNode, inputSemanticModel, compilation, projectDirectory,
                                                            emittedUsings, emittedExterns);

                    var richGenerator = generator as IRichCodeGenerator ?? new EnrichingCodeGeneratorProxy(generator);

                    var emitted = await richGenerator.GenerateRichAsync(context, progress, CancellationToken.None);

                    emittedExterns        = emittedExterns.AddRange(emitted.Externs);
                    emittedUsings         = emittedUsings.AddRange(emitted.Usings);
                    emittedAttributeLists = emittedAttributeLists.AddRange(emitted.AttributeLists);
                    emittedMembers        = emittedMembers.AddRange(emitted.Members);
                }
            }

            var compilationUnit =
                SyntaxFactory.CompilationUnit(
                    SyntaxFactory.List(emittedExterns),
                    SyntaxFactory.List(emittedUsings),
                    SyntaxFactory.List(emittedAttributeLists),
                    SyntaxFactory.List(emittedMembers))
                .WithLeadingTrivia(SyntaxFactory.Comment(GeneratedByAToolPreamble))
                .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed)
                .NormalizeWhitespace();

            return(compilationUnit.SyntaxTree);
        }
Esempio n. 2
0
".Replace("\r\n", "\n").Replace("\n", Environment.NewLine); // normalize regardless of git checkout policy

        /// <summary>
        /// Produces a new document in response to any code generation attributes found in the specified document.
        /// </summary>
        /// <param name="compilation">The compilation to which the document belongs.</param>
        /// <param name="inputDocument">The document to scan for generator attributes.</param>
        /// <param name="assemblyLoader">A function that can load an assembly with the given name.</param>
        /// <param name="progress">Reports warnings and errors in code generation.</param>
        /// <returns>A task whose result is the generated document.</returns>
        public static async Task <SyntaxTree> TransformAsync(CSharpCompilation compilation, SyntaxTree inputDocument, Func <AssemblyName, Assembly> assemblyLoader, IProgress <Diagnostic> progress)
        {
            Requires.NotNull(compilation, nameof(compilation));
            Requires.NotNull(inputDocument, nameof(inputDocument));
            Requires.NotNull(assemblyLoader, nameof(assemblyLoader));

            var inputSemanticModel = compilation.GetSemanticModel(inputDocument);
            var inputSyntaxTree    = inputSemanticModel.SyntaxTree;

            var inputFileLevelUsingDirectives = inputSyntaxTree.GetRoot().ChildNodes().OfType <UsingDirectiveSyntax>();

            var memberNodes = from syntax in inputSyntaxTree.GetRoot().DescendantNodes(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax || n is TypeDeclarationSyntax).OfType <MemberDeclarationSyntax>()
                              select syntax;

            var emittedMembers = SyntaxFactory.List <MemberDeclarationSyntax>();

            foreach (var memberNode in memberNodes)
            {
                var generators = FindCodeGenerators(inputSemanticModel, memberNode, assemblyLoader);
                foreach (var generator in generators)
                {
                    var context        = new TransformationContext(memberNode, inputSemanticModel, compilation);
                    var generatedTypes = await generator.GenerateAsync(context, progress, CancellationToken.None);

                    // Figure out ancestry for the generated type, including nesting types and namespaces.
                    foreach (var ancestor in memberNode.Ancestors())
                    {
                        var ancestorNamespace = ancestor as NamespaceDeclarationSyntax;
                        var nestingClass      = ancestor as ClassDeclarationSyntax;
                        var nestingStruct     = ancestor as StructDeclarationSyntax;
                        if (ancestorNamespace != null)
                        {
                            generatedTypes = SyntaxFactory.SingletonList <MemberDeclarationSyntax>(
                                ancestorNamespace
                                .WithMembers(generatedTypes)
                                .WithLeadingTrivia(SyntaxFactory.TriviaList())
                                .WithTrailingTrivia(SyntaxFactory.TriviaList()));
                        }
                        else if (nestingClass != null)
                        {
                            generatedTypes = SyntaxFactory.SingletonList <MemberDeclarationSyntax>(
                                nestingClass
                                .WithMembers(generatedTypes)
                                .WithLeadingTrivia(SyntaxFactory.TriviaList())
                                .WithTrailingTrivia(SyntaxFactory.TriviaList()));
                        }
                        else if (nestingStruct != null)
                        {
                            generatedTypes = SyntaxFactory.SingletonList <MemberDeclarationSyntax>(
                                nestingStruct
                                .WithMembers(generatedTypes)
                                .WithLeadingTrivia(SyntaxFactory.TriviaList())
                                .WithTrailingTrivia(SyntaxFactory.TriviaList()));
                        }
                    }

                    emittedMembers = emittedMembers.AddRange(generatedTypes);
                }
            }

            // By default, retain all the using directives that came from the input file.
            var resultFileLevelUsingDirectives = SyntaxFactory.List(inputFileLevelUsingDirectives);

            var compilationUnit = SyntaxFactory.CompilationUnit()
                                  .WithUsings(resultFileLevelUsingDirectives)
                                  .WithMembers(emittedMembers)
                                  .WithLeadingTrivia(SyntaxFactory.Comment(GeneratedByAToolPreamble))
                                  .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed)
                                  .NormalizeWhitespace();

            return(compilationUnit.SyntaxTree);
        }