public async Task <IEnumerable <AnalysisResult> > AnalyzeScopeAsync(IProgress <int> progress) { var analysisResults = new ConcurrentBag <AnalysisResult>(); SyntaxTree syntaxTree = null; SemanticModel semanticModel = null; SingleSyntaxTreeAnalysisContext analysisContext = null; var analyzeSyntaxTreeActions = AnalyzeSingleSyntaxTreeAndCollectResultsActions // We intentionally access the modified closure here (syntaxTree, semanticModel, analysisContext), // because we want to avoid creation of a huge number of temporary Action objects. // ReSharper disable AccessToModifiedClosure .Select(action => new Action(() => action(syntaxTree, semanticModel, analysisContext, analysisResults))) // ReSharper restore AccessToModifiedClosure .ToArray(); // Same here. We want to have just a single Action object created and called many times. // We intentionally do not want to use a local function here. Although its usage would be // semantically nicer and create exactly the same closure as the below Action, the fact that // we need to convert that local function to Action in the Task.Run() call means we would // end up in creating an additional Action object for every pass in the loop, and that's // exactly what we want to avoid. // ReSharper disable once ConvertToLocalFunction Action analyzeSyntaxTreeInParallel = () => Parallel.Invoke(analyzeSyntaxTreeActions); // WARNING: Keep the progress counter in sync with the logic behind the calculation of the maximum progress! int progressCounter = 0; foreach (var document in GetDocumentsToAnalyze()) { analysisContext = new SingleSyntaxTreeAnalysisContext(document); syntaxTree = await document.GetSyntaxTreeAsync().ConfigureAwait(false); if (!syntaxTree.BeginsWithAutoGeneratedComment()) { semanticModel = await document.GetSemanticModelAsync(); // Each of the actions (analysis) will operate on the same (current) syntaxTree and semanticModel. await Task.Run(analyzeSyntaxTreeInParallel); } progress.Report(++progressCounter); } return(analysisResults); }
public async Task <IEnumerable <AnalysisResult> > AnalyzeScopeAsync(IProgress <int> progress) { var analysisResults = new ConcurrentBag <AnalysisResult>(); var potentialDuplicates = new ConcurrentBag <AnalysisResult>(); SyntaxTree syntaxTree = null; SemanticModel semanticModel = null; SingleSyntaxTreeAnalysisContext analysisContext = null; var analyzeSyntaxTreeActions = AnalyzeSingleSyntaxTreeAndCollectResultsActions // We intentionally access the modified closure here (syntaxTree, semanticModel, analysisContext), // because we want to avoid creation of a huge number of temporary Action objects. // ReSharper disable AccessToModifiedClosure .Select(action => new Action(() => action(syntaxTree, semanticModel, analysisContext, analysisResults, potentialDuplicates))) // ReSharper restore AccessToModifiedClosure .ToArray(); // Same here. We want to have just a single Action object created and called many times. // We intentionally do not want to use a local function here. Although its usage would be // semantically nicer and create exactly the same closure as the below Action, the fact that // we need to convert that local function to Action in the Task.Run() call means we would // end up in creating an additional Action object for every pass in the loop, and that's // exactly what we want to avoid. // ReSharper disable once ConvertToLocalFunction Action analyzeSyntaxTreeInParallel = () => Parallel.Invoke(analyzeSyntaxTreeActions); // WARNING: Keep the progress counter in sync with the logic behind the calculation of the maximum progress! int progressCounter = 0; foreach (var document in GetDocumentsToAnalyze()) { analysisContext = new SingleSyntaxTreeAnalysisContext(document); syntaxTree = await document.GetSyntaxTreeAsync().ConfigureAwait(false); if (!syntaxTree.BeginsWithAutoGeneratedComment()) { semanticModel = await document.GetSemanticModelAsync(); // Each of the actions (analysis) will operate on the same (current) syntaxTree and semanticModel. await Task.Run(analyzeSyntaxTreeInParallel); } progress.Report(++progressCounter); } // TODO-IG: Fully refactor Analysis/Scope/Analyzer/Context/Result etc. // and remove this terrible temporary workaround. var duplicatesToRemove = FindDuplicatesToRemove(); return(analysisResults.Except(duplicatesToRemove)); IReadOnlyCollection <AnalysisResult> FindDuplicatesToRemove() { return(potentialDuplicates // We consider the result to be a duplicate if have the same // suggestion on the same node several times. // The AnalysisResult does not contain node (at the moment, // who knows what the upcoming refactoring will bring us ;-)) // so we will see if the file name and the position are the same. .GroupBy(result => new { result.Suggestion, result.FilePath, result.Position }) .Where(group => group.Count() > 1) // Just leave the first one so far and mark the rest as those to be removed. // This is all a temporary workaround after all :-) .SelectMany(group => group.Skip(1)) .ToList()); } }