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