Beispiel #1
0
        public GeneratorDriver RunFullGeneration(Compilation compilation, out Compilation outputCompilation, out ImmutableArray <Diagnostic> diagnostics, CancellationToken cancellationToken = default)
        {
            // with no generators, there is no work to do
            if (_state.Generators.Length == 0)
            {
                outputCompilation = compilation;
                diagnostics       = ImmutableArray <Diagnostic> .Empty;
                return(this);
            }

            // run the actual generation
            var state        = StateWithPendingEditsApplied(_state);
            var stateBuilder = PooledDictionary <ISourceGenerator, GeneratorState> .GetInstance();

            var receivers = PooledDictionary <ISourceGenerator, ISyntaxReceiver> .GetInstance();

            var diagnosticsBag = new DiagnosticBag();

            foreach (var generator in state.Generators)
            {
                // initialize the generator if needed
                if (!state.GeneratorStates.TryGetValue(generator, out GeneratorState generatorState))
                {
                    generatorState = InitializeGenerator(generator, diagnosticsBag, cancellationToken);
                }

                if (generatorState.Info.Initialized)
                {
                    stateBuilder.Add(generator, generatorState);
                }

                // create the syntax receiver if requested
                if (generatorState.Info.SyntaxReceiverCreator is object)
                {
                    var rx = generatorState.Info.SyntaxReceiverCreator();
                    receivers.Add(generator, rx);
                }
            }

            // Run a syntax walk if any of the generators requested it
            if (receivers.Count > 0)
            {
                GeneratorSyntaxWalker walker = new GeneratorSyntaxWalker(receivers.Values.ToImmutableArray());
                foreach (var syntaxTree in compilation.SyntaxTrees)
                {
                    walker.Visit(syntaxTree.GetRoot());
                }
            }

            // https://github.com/dotnet/roslyn/issues/42629: should be possible to parallelize this
            foreach (var(generator, generatorState) in stateBuilder.ToImmutableArray())
            {
                try
                {
                    // we create a new context for each run of the generator. We'll never re-use existing state, only replace anything we have
                    _ = receivers.TryGetValue(generator, out var syntaxReceiverOpt);
                    var context = new SourceGeneratorContext(compilation, state.AdditionalTexts.NullToEmpty(), syntaxReceiverOpt, diagnosticsBag);
                    generator.Execute(context);
                    stateBuilder[generator] = generatorState.WithSources(ParseAdditionalSources(context.AdditionalSources.ToImmutableAndFree(), cancellationToken));
                }
                catch
                {
                    diagnosticsBag.Add(Diagnostic.Create(MessageProvider, MessageProvider.WRN_GeneratorFailedDuringGeneration, generator.GetType().Name));
                }
            }
            state       = state.With(generatorStates: stateBuilder.ToImmutableDictionaryAndFree());
            diagnostics = diagnosticsBag.ToReadOnlyAndFree();

            // build the final state, and return
            return(BuildFinalCompilation(compilation, out outputCompilation, state, cancellationToken));
        }
Beispiel #2
0
        internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, DiagnosticBag?diagnosticsBag, CancellationToken cancellationToken = default)
        {
            // with no generators, there is no work to do
            if (_state.Generators.IsEmpty)
            {
                return(_state);
            }

            // run the actual generation
            var state        = StateWithPendingEditsApplied(_state);
            var stateBuilder = ArrayBuilder <GeneratorState> .GetInstance(state.Generators.Length);

            var walkerBuilder = ArrayBuilder <GeneratorSyntaxWalker?> .GetInstance(state.Generators.Length, fillWithValue : null); // we know there is at max 1 per generator

            int receiverCount = 0;

            for (int i = 0; i < state.Generators.Length; i++)
            {
                var generator      = state.Generators[i];
                var generatorState = state.GeneratorStates[i];

                // initialize the generator if needed
                if (!generatorState.Info.Initialized)
                {
                    InitializationContext context = new InitializationContext(cancellationToken);
                    Exception?            ex      = null;
                    try
                    {
                        generator.Initialize(context);
                    }
                    catch (Exception e)
                    {
                        ex = e;
                    }
                    generatorState = ex is null
                                     ? new GeneratorState(context.InfoBuilder.ToImmutable())
                                     : SetGeneratorException(MessageProvider, GeneratorState.Uninitialized, generator, ex, diagnosticsBag, isInit: true);
                }

                // create the syntax receiver if requested
                if (generatorState.Info.SyntaxReceiverCreator is object)
                {
                    try
                    {
                        var rx = generatorState.Info.SyntaxReceiverCreator();
                        walkerBuilder.SetItem(i, new GeneratorSyntaxWalker(rx));
                        generatorState = generatorState.WithReceiver(rx);
                        receiverCount++;
                    }
                    catch (Exception e)
                    {
                        generatorState = SetGeneratorException(MessageProvider, generatorState, generator, e, diagnosticsBag);
                    }
                }

                stateBuilder.Add(generatorState);
            }


            // Run a syntax walk if any of the generators requested it
            if (receiverCount > 0)
            {
                foreach (var tree in compilation.SyntaxTrees)
                {
                    var root = tree.GetRoot(cancellationToken);

                    // https://github.com/dotnet/roslyn/issues/42629: should be possible to parallelize this
                    for (int i = 0; i < walkerBuilder.Count; i++)
                    {
                        var walker = walkerBuilder[i];
                        if (walker is object)
                        {
                            try
                            {
                                walker.Visit(root);
                            }
                            catch (Exception e)
                            {
                                stateBuilder[i] = SetGeneratorException(MessageProvider, stateBuilder[i], state.Generators[i], e, diagnosticsBag);
                                walkerBuilder.SetItem(i, null); // don't re-visit this walker for any other trees
                            }
                        }
                    }
                }
            }
            walkerBuilder.Free();

            // https://github.com/dotnet/roslyn/issues/42629: should be possible to parallelize this
            for (int i = 0; i < state.Generators.Length; i++)
            {
                var generator      = state.Generators[i];
                var generatorState = stateBuilder[i];

                // don't try and generate if initialization or syntax walk failed
                if (generatorState.Exception is object)
                {
                    continue;
                }
                Debug.Assert(generatorState.Info.Initialized);

                // we create a new context for each run of the generator. We'll never re-use existing state, only replace anything we have
                var context = new SourceGeneratorContext(compilation, state.AdditionalTexts.NullToEmpty(), state.OptionsProvider, generatorState.SyntaxReceiver);
                try
                {
                    generator.Execute(context);
                }
                catch (Exception e)
                {
                    stateBuilder[i] = SetGeneratorException(MessageProvider, generatorState, generator, e, diagnosticsBag);
                    continue;
                }

                (var sources, var diagnostics) = context.ToImmutableAndFree();
                stateBuilder[i] = new GeneratorState(generatorState.Info, sources, ParseAdditionalSources(generator, sources, cancellationToken), diagnostics);
                diagnosticsBag?.AddRange(diagnostics);
            }
            state = state.With(generatorStates: stateBuilder.ToImmutableAndFree());
            return(state);
        }