private InprogressState GetOrCreateInprogressState() { if (_state == null) { _state = new InprogressState(this); } return(_state); }
private InprogressState ClearInprogressState() { lock (_gate) { var state = _stateDoNotAccessDirectly; _stateDoNotAccessDirectly = null; return(state); } }
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); } }
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); } }
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; }
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); }
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); }
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); } }