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