private static IEnumerable <T> Visit <T>(SyntaxTree syntaxTreeAsync, Microsoft.CodeAnalysis.Compilation compile, ICompilationInfo compilationInfo, ICompilationDocument document) { CheckClassVisitor visitor; if (!CompilationDocumentResult.TryGetValue(document.FullName, out visitor)) { var semanticModel = compile.GetSemanticModel(syntaxTreeAsync); visitor = new CheckClassVisitor(document, semanticModel, compilationInfo); visitor.Visit(syntaxTreeAsync.GetRoot()); CompilationDocumentResult.Add(document.FullName, visitor); } return(visitor.Get <T>()); }
public IStateTable GetSyntaxInputTable(SyntaxInputNode syntaxInputNode, NodeStateTable <SyntaxTree> syntaxTreeTable) { Debug.Assert(_syntaxInputNodes.Contains(syntaxInputNode)); // when we don't have a value for this node, we update all the syntax inputs at once if (!_tableBuilder.Contains(syntaxInputNode)) { // CONSIDER: when the compilation is the same as previous, the syntax trees must also be the same. // if we have a previous state table for a node, we can just short circuit knowing that it is up to date // This step isn't part of the tree, so we can skip recording. var compilationIsCached = _compilation == _previous._compilation; // get a builder for each input node var syntaxInputBuilders = ArrayBuilder <(SyntaxInputNode node, ISyntaxInputBuilder builder)> .GetInstance(_syntaxInputNodes.Length); foreach (var node in _syntaxInputNodes) { // We don't cache the tracked incremental steps in a manner that we can easily rehydrate between runs, // so we disable the cached compilation perf optimization when incremental step tracking is enabled. if (compilationIsCached && !_enableTracking && _previous._tables.TryGetValue(node, out var previousStateTable)) { _tableBuilder.SetTable(node, previousStateTable); } else { syntaxInputBuilders.Add((node, node.GetBuilder(_previous._tables, _enableTracking))); _syntaxTimes[node] = TimeSpan.Zero; } } if (syntaxInputBuilders.Count > 0) { // at this point we need to grab the syntax trees from the new compilation, and optionally diff them against the old ones NodeStateTable <SyntaxTree> syntaxTreeState = syntaxTreeTable; // update each tree for the builders, sharing the semantic model foreach (var(tree, state, syntaxTreeIndex, stepInfo) in syntaxTreeState) { var root = new Lazy <SyntaxNode>(() => tree.GetRoot(_cancellationToken)); var model = state != EntryState.Removed ? new Lazy <SemanticModel>(() => _compilation.GetSemanticModel(tree)) : null; for (int i = 0; i < syntaxInputBuilders.Count; i++) { var currentNode = syntaxInputBuilders[i].node; try { var sw = SharedStopwatch.StartNew(); try { _cancellationToken.ThrowIfCancellationRequested(); syntaxInputBuilders[i].builder.VisitTree(root, state, model, _cancellationToken); } finally { var elapsed = sw.Elapsed; // if this node isn't the one that caused the update, ensure we remember it and remove the time it took from the requester if (currentNode != syntaxInputNode) { _syntaxTimes[syntaxInputNode] = _syntaxTimes[syntaxInputNode].Subtract(elapsed); _syntaxTimes[currentNode] = _syntaxTimes[currentNode].Add(elapsed); } } } catch (UserFunctionException ufe) { // we're evaluating this node ahead of time, so we can't just throw the exception // instead we'll hold onto it, and throw the exception when a downstream node actually // attempts to read the value _syntaxExceptions[currentNode] = ufe; syntaxInputBuilders.RemoveAt(i); i--; } } } // save the updated inputs foreach ((var node, ISyntaxInputBuilder builder) in syntaxInputBuilders) { builder.SaveStateAndFree(_tableBuilder); Debug.Assert(_tableBuilder.Contains(node)); } } syntaxInputBuilders.Free(); } // if we don't have an entry for this node, it must have thrown an exception if (!_tableBuilder.TryGetTable(syntaxInputNode, out var result)) { throw _syntaxExceptions[syntaxInputNode]; } return(result); }
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) { var context = new GeneratorInitializationContext(cancellationToken); Exception?ex = null; try { generator.Initialize(context); } catch (Exception e) when(FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { 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.SyntaxContextReceiverCreator is object) { ISyntaxContextReceiver?rx = null; try { rx = generatorState.Info.SyntaxContextReceiverCreator(); } catch (Exception e) { generatorState = SetGeneratorException(MessageProvider, generatorState, generator, e, diagnosticsBag); } if (rx is object) { walkerBuilder.SetItem(i, new GeneratorSyntaxWalker(rx)); generatorState = generatorState.WithReceiver(rx); receiverCount++; } } 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); var semanticModel = compilation.GetSemanticModel(tree); // 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.VisitWithModel(semanticModel, 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 GeneratorExecutionContext(compilation, state.ParseOptions, state.AdditionalTexts.NullToEmpty(), state.OptionsProvider, generatorState.SyntaxReceiver, CreateSourcesCollection(), cancellationToken); try { generator.Execute(context); } catch (Exception e) when(FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { 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); }