Exemple #1
0
                private async Task <ProjectAnalyzersAndStates> GetOrCreateProjectAnalyzersAndStatesAsync(Project project, CancellationToken cancellationToken)
                {
                    // Update per-project analyzers if needed.
                    IEnumerable <Tuple <DiagnosticState, ProviderId, StateType> > removedStates;
                    ProjectAnalyzersAndStates projectAnalyzersAndStates = this.GetOrCreateProjectAnalyzersAndStates(project, out removedStates);

                    if (removedStates.Any())
                    {
                        // Analyzers got updated, so clear the existing persistent states.
                        await this.owner.RemoveCacheDataAsync(project, removedStates, cancellationToken).ConfigureAwait(false);
                    }

                    return(projectAnalyzersAndStates);
                }
Exemple #2
0
                private ProjectAnalyzersAndStates GetOrCreateProjectAnalyzersAndStates(Project project,
                                                                                       out IEnumerable <Tuple <DiagnosticState, ProviderId, StateType> > removedStates)
                {
                    removedStates = SpecializedCollections.EmptyEnumerable <Tuple <DiagnosticState, ProviderId, StateType> >();

                    var newAnalyzersBuilder = ImmutableArray.CreateBuilder <KeyValuePair <string, IEnumerable <DiagnosticAnalyzer> > >();

                    foreach (var analyzerReference in project.AnalyzerReferences)
                    {
                        // Filter out duplicate analyzer references.
                        if (this.sharedAnalyzersAndStates.HasAnalyzerReference(analyzerReference, project.Language))
                        {
                            continue;
                        }

                        // Get analyzers in the analyzer reference for the given project language.
                        // Filter out duplicate analyzers.
                        // NOTE: The HasAnalyzerReference check above to filter out duplicate analyzer references works only for duplicate AnalyzerFileReference with the same underlying assembly.
                        // However, we can also have AnalyzerImageReference, which might contain the same DiagnosticAnalyzer instance across different AnalyzerImageReference instances
                        // and we want to avoid duplicate analyzers for that case. Hence we apply the HasAnalyzer filter here.
                        var analyzers = analyzerReference.GetAnalyzers(project.Language)
                                        .Where(a => !this.sharedAnalyzersAndStates.HasAnalyzer(a, project.Language));

                        if (analyzers.Any())
                        {
                            newAnalyzersBuilder.Add(KeyValuePair.Create(analyzerReference.Display, analyzers));
                        }
                    }

                    var newAnalyzers = newAnalyzersBuilder.ToImmutable();
                    ProjectAnalyzersAndStates newProjectAnalyzersAndStates = null;

                    ProjectAnalyzersAndStates currentProjectAnalyzersAndStates;

                    if (this.projectAnalyzersAndStatesMap.TryGetValue(project.Id, out currentProjectAnalyzersAndStates))
                    {
                        var newAnalyzersCount = newAnalyzers.Sum(kv => kv.Value.Count());
                        if (currentProjectAnalyzersAndStates != null && currentProjectAnalyzersAndStates.AnalyzerCount == newAnalyzersCount)
                        {
                            Contract.ThrowIfFalse(currentProjectAnalyzersAndStates.AnalyzerCount > 0);

                            // Project still has the same number of analyzers, does the saved projectAnalyzersAndStates has the same set of analyzers?
                            var hasSameAnalyzers = true;
                            foreach (var analyzerPair in newAnalyzers)
                            {
                                foreach (var analyzer in analyzerPair.Value)
                                {
                                    if (!currentProjectAnalyzersAndStates.HasAnalyzer(analyzer))
                                    {
                                        hasSameAnalyzers = false;
                                        break;
                                    }
                                }
                            }

                            if (hasSameAnalyzers)
                            {
                                return(currentProjectAnalyzersAndStates);
                            }
                        }
                    }
                    else
                    {
                        currentProjectAnalyzersAndStates = null;
                    }

                    if (currentProjectAnalyzersAndStates != null)
                    {
                        removedStates = currentProjectAnalyzersAndStates.GetAllExistingDiagnosticStates();
                    }

                    var workspaceAnalyzersCount = this.sharedAnalyzersAndStates.GetAnalyzerCount(project.Language);

                    newProjectAnalyzersAndStates = ProjectAnalyzersAndStates.CreateIfAnyAnalyzers(newAnalyzers, workspaceAnalyzersCount, project.Language);
                    return(this.projectAnalyzersAndStatesMap.AddOrUpdate(project.Id, newProjectAnalyzersAndStates, (k, c) => newProjectAnalyzersAndStates));
                }
Exemple #3
0
            private ProjectAnalyzersAndStates GetOrCreateProjectAnalyzersAndStates(Project project,
                                                                                   out IEnumerable <Tuple <DiagnosticState, ProviderId, StateType> > removedStates)
            {
                removedStates = SpecializedCollections.EmptyEnumerable <Tuple <DiagnosticState, ProviderId, StateType> >();

                var newAnalyzersBuilder = ImmutableArray.CreateBuilder <KeyValuePair <string, IEnumerable <DiagnosticAnalyzer> > >();

                foreach (var analyzerReference in project.AnalyzerReferences)
                {
                    // Filter out duplicate analyzer references.
                    if (_sharedAnalyzersAndStates.HasAnalyzerReference(analyzerReference, project.Language))
                    {
                        continue;
                    }

                    // Get analyzers in the analyzer reference for the given project language.
                    // Filter out duplicate analyzers.
                    // NOTE: The HasAnalyzerReference check above to filter out duplicate analyzer references works only for duplicate AnalyzerFileReference with the same underlying assembly.
                    // However, we can also have AnalyzerImageReference, which might contain the same DiagnosticAnalyzer instance across different AnalyzerImageReference instances
                    // and we want to avoid duplicate analyzers for that case. Hence we apply the HasAnalyzer filter here.
                    var analyzers = analyzerReference.GetAnalyzers(project.Language)
                                    .Where(a => !_sharedAnalyzersAndStates.HasAnalyzer(a, project.Language));

                    if (analyzers.Any())
                    {
                        newAnalyzersBuilder.Add(KeyValuePair.Create(analyzerReference.Display, analyzers));
                    }
                }

                var newAnalyzers = newAnalyzersBuilder.ToImmutable();
                ProjectAnalyzersAndStates newProjectAnalyzersAndStates = null;

                ProjectAnalyzersAndStates currentProjectAnalyzersAndStates;

                if (_projectAnalyzersAndStatesMap.TryGetValue(project.Id, out currentProjectAnalyzersAndStates))
                {
                    var newAnalyzersCount = newAnalyzers.Sum(kv => kv.Value.Count());
                    if (currentProjectAnalyzersAndStates != null && currentProjectAnalyzersAndStates.AnalyzerCount == newAnalyzersCount)
                    {
                        Contract.ThrowIfFalse(currentProjectAnalyzersAndStates.AnalyzerCount > 0);

                        // Project still has the same number of analyzers, does the saved projectAnalyzersAndStates has the same set of analyzers?
                        var hasSameAnalyzers = true;
                        foreach (var analyzerPair in newAnalyzers)
                        {
                            foreach (var analyzer in analyzerPair.Value)
                            {
                                if (!currentProjectAnalyzersAndStates.HasAnalyzer(analyzer))
                                {
                                    hasSameAnalyzers = false;
                                    break;
                                }
                            }
                        }

                        if (hasSameAnalyzers)
                        {
                            return(currentProjectAnalyzersAndStates);
                        }
                    }
                }
                else
                {
                    currentProjectAnalyzersAndStates = null;
                }

                if (currentProjectAnalyzersAndStates != null)
                {
                    removedStates = currentProjectAnalyzersAndStates.GetAllExistingDiagnosticStates();
                }

                var workspaceAnalyzersCount = _sharedAnalyzersAndStates.GetAnalyzerCount(project.Language);

                newProjectAnalyzersAndStates = ProjectAnalyzersAndStates.CreateIfAnyAnalyzers(newAnalyzers, workspaceAnalyzersCount, project.Language);

                // this cache logic is completely broken. cache can't be used in this way. if we didn't have an issue before that is just purely by luck.
                // the reason it is broken is that, this method can be called from any place (no central call path so, can't figure out all the possible paths, but I confirmed that there
                // are at least more than one path) from any workspace snapshot from any thread but the cache doesn't have any versioning or snapshot check,
                // but regardless, this code updates the cache. which introduce a race where if code uses the cache in 2 different places in the same method, the return value might be different.
                // and that is causing us to crash since one gets two completely different state.
                //
                // I tried to figure out how to fix this but, eventually gave up since it is so deeply spread in the code, I don't see any way to properly fix this.
                // my conculusion is ripping out this code and re-write this state management code.

                return(_projectAnalyzersAndStatesMap.AddOrUpdate(project.Id, newProjectAnalyzersAndStates, (k, c) => newProjectAnalyzersAndStates));
            }