예제 #1
0
        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>());
        }
예제 #2
0
            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);
            }
예제 #3
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)
                {
                    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);
        }