Exemplo n.º 1
0
        private InprogressState GetOrCreateInprogressState()
        {
            if (_state == null)
            {
                _state = new InprogressState(this);
            }

            return(_state);
        }
Exemplo n.º 2
0
        private InprogressState ClearInprogressState()
        {
            lock (_gate)
            {
                var state = _stateDoNotAccessDirectly;

                _stateDoNotAccessDirectly = null;
                return(state);
            }
        }
Exemplo n.º 3
0
        private static async System.Threading.Tasks.Task CleanupAllLiveErrors(
            DiagnosticAnalyzerService diagnosticService, IDisposable batchUpdateToken,
            Solution solution, InprogressState state, IEnumerable <Project> projects)
        {
            foreach (var project in projects)
            {
                foreach (var document in project.Documents)
                {
                    await SynchronizeWithBuildAsync(diagnosticService, batchUpdateToken, document, ImmutableArray <DiagnosticData> .Empty).ConfigureAwait(false);
                }

                await SynchronizeWithBuildAsync(diagnosticService, batchUpdateToken, project, ImmutableArray <DiagnosticData> .Empty).ConfigureAwait(false);
            }
        }
Exemplo n.º 4
0
        private InprogressState GetOrCreateInprogressState()
        {
            lock (_gate)
            {
                if (_stateDoNotAccessDirectly == null)
                {
                    // here, we take current snapshot of solution when the state is first created. and through out this code, we use this snapshot.
                    // since we have no idea what actual snapshot of solution the out of proc build has picked up, it doesn't remove the race we can have
                    // between build and diagnostic service, but this at least make us to consistent inside of our code.
                    _stateDoNotAccessDirectly = new InprogressState(this, _workspace.CurrentSolution);
                }

                return(_stateDoNotAccessDirectly);
            }
        }
Exemplo n.º 5
0
        private async System.Threading.Tasks.Task CleanupAllLiveErrorsIfNeededAsync(
            DiagnosticAnalyzerService diagnosticService, IDisposable batchUpdateToken,
            Solution solution, InprogressState state)
        {
            if (_workspace.Options.GetOption(InternalDiagnosticsOptions.BuildErrorIsTheGod))
            {
                await CleanupAllLiveErrors(diagnosticService, batchUpdateToken, solution, state, solution.Projects).ConfigureAwait(false);

                return;
            }

            if (_workspace.Options.GetOption(InternalDiagnosticsOptions.ClearLiveErrorsForProjectBuilt))
            {
                await CleanupAllLiveErrors(diagnosticService, batchUpdateToken, solution, state, state.GetProjectsBuilt(solution)).ConfigureAwait(false);

                return;
            }

            await CleanupAllLiveErrors(diagnosticService, batchUpdateToken, solution, state, state.GetProjectsWithoutErrors(solution)).ConfigureAwait(false);

            return;
        }
Exemplo n.º 6
0
        private Dictionary <ProjectId, HashSet <string> > GetSupportedLiveDiagnosticId(Solution solution, InprogressState state)
        {
            var map = new Dictionary <ProjectId, HashSet <string> >();

            // here, we don't care about perf that much since build is already expensive work
            foreach (var projectId in state.GetProjectsWithErrors(solution))
            {
                var project = solution.GetProject(projectId);
                if (project == null)
                {
                    continue;
                }

                var descriptorMap = _diagnosticService.GetDiagnosticDescriptors(project);
                map.Add(project.Id, new HashSet <string>(descriptorMap.Values.SelectMany(v => v.Select(d => d.Id))));
            }

            return(map);
        }
Exemplo n.º 7
0
        internal void OnSolutionBuild(object sender, UIContextChangedEventArgs e)
        {
            if (e.Activated)
            {
                // build just started, create the state and fire build in progress event.
                var state = GetOrCreateInprogressState();
                return;
            }

            // get local copy of inprogress state
            var inprogressState = _state;

            // building is done. reset the state.
            _state = null;

            // enqueue build/live sync in the queue.
            var asyncToken = _listener.BeginAsyncOperation("OnSolutionBuild");

            _taskQueue.ScheduleTask(async() =>
            {
                // nothing to do
                if (inprogressState == null)
                {
                    return;
                }

                _lastBuiltResult = inprogressState.GetBuildDiagnostics();

                // we are about to update live analyzer data using one from build.
                // pause live analyzer
                using (var operation = _notificationService.Start("BuildDone"))
                {
                    // we will have a race here since we can't track version of solution the out of proc build actually used.
                    // result of the race will be us dropping some diagnostics from the build to the floor.
                    var solution = _workspace.CurrentSolution;

                    var supportedIdMap = GetSupportedLiveDiagnosticId(solution, inprogressState);
                    Func <DiagnosticData, bool> liveDiagnosticChecker = d =>
                    {
                        // REVIEW: we probably need a better design on de-duplicating live and build errors. or don't de-dup at all.
                        //         for now, we are special casing compiler error case.
                        var project = solution.GetProject(d.ProjectId);
                        if (project == null)
                        {
                            // project doesn't exist
                            return(false);
                        }

                        // REVIEW: current design is that we special case compiler analyzer case and we accept only document level
                        //         diagnostic as live. otherwise, we let them be build errors. we changed compiler analyzer accordingly as well
                        //         so that it doesn't report project level diagnostic as live errors.
                        if (_diagnosticService.IsCompilerDiagnostic(project.Language, d) && d.DocumentId == null)
                        {
                            // compiler error but project level error
                            return(false);
                        }

                        HashSet <string> set;
                        if (supportedIdMap.TryGetValue(d.ProjectId, out set) && set.Contains(d.Id))
                        {
                            return(true);
                        }

                        return(false);
                    };

                    var diagnosticService = _diagnosticService as DiagnosticAnalyzerService;
                    if (diagnosticService != null)
                    {
                        await CleanupAllLiveErrorsIfNeededAsync(diagnosticService, solution, inprogressState).ConfigureAwait(false);
                        await SyncBuildErrorsAndReportAsync(diagnosticService, inprogressState.GetLiveDiagnosticsPerProject(liveDiagnosticChecker)).ConfigureAwait(false);
                    }

                    inprogressState.Done();
                }
            }).CompletesAsyncOperation(asyncToken);
        }
Exemplo n.º 8
0
        private async System.Threading.Tasks.Task SyncBuildErrorsAndReportAsync(DiagnosticAnalyzerService diagnosticService, InprogressState inprogressState)
        {
            var solution = inprogressState.Solution;
            var map      = await inprogressState.GetLiveDiagnosticsPerProjectAsync().ConfigureAwait(false);

            // make those errors live errors
            await diagnosticService.SynchronizeWithBuildAsync(_workspace, map).ConfigureAwait(false);

            // raise events for ones left-out
            var buildErrors = GetBuildErrors().Except(map.Values.SelectMany(v => v)).GroupBy(k => k.DocumentId);

            foreach (var group in buildErrors)
            {
                if (group.Key == null)
                {
                    foreach (var projectGroup in group.GroupBy(g => g.ProjectId))
                    {
                        Contract.ThrowIfNull(projectGroup.Key);
                        ReportBuildErrors(projectGroup.Key, solution, projectGroup.ToImmutableArray());
                    }

                    continue;
                }

                ReportBuildErrors(group.Key, solution, group.ToImmutableArray());
            }
        }
        private async System.Threading.Tasks.Task CleanupAllLiveErrorsIfNeededAsync(Solution solution, InprogressState state)
        {
            var buildErrorIsTheGod = _workspace.Options.GetOption(InternalDiagnosticsOptions.BuildErrorIsTheGod);
            var clearProjectErrors = _workspace.Options.GetOption(InternalDiagnosticsOptions.ClearLiveErrorsForProjectBuilt);

            if (!buildErrorIsTheGod && !clearProjectErrors)
            {
                return;
            }

            var projects = buildErrorIsTheGod ? solution.Projects :
                           (clearProjectErrors ? state.GetProjectsBuilt(solution) : SpecializedCollections.EmptyEnumerable <Project>());

            // clear all live errors
            foreach (var project in projects)
            {
                foreach (var document in project.Documents)
                {
                    await SynchronizeWithBuildAsync(document, ImmutableArray <DiagnosticData> .Empty).ConfigureAwait(false);
                }

                await SynchronizeWithBuildAsync(project, ImmutableArray <DiagnosticData> .Empty).ConfigureAwait(false);
            }
        }