Esempio n. 1
0
        private async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > ComputeDiagnosticsAsync(
            CompilationWithAnalyzers?compilationWithAnalyzers, Project project, ImmutableArray <DiagnosticAnalyzer> ideAnalyzers, bool forcedAnalysis, CancellationToken cancellationToken)
        {
            try
            {
                var result = ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> .Empty;

                // can be null if given project doesn't support compilation.
                if (compilationWithAnalyzers?.Analyzers.Length > 0)
                {
                    // calculate regular diagnostic analyzers diagnostics
                    var resultMap = await _diagnosticAnalyzerRunner.AnalyzeProjectAsync(project, compilationWithAnalyzers,
                                                                                        forcedAnalysis, logPerformanceInfo : true, getTelemetryInfo : true, cancellationToken).ConfigureAwait(false);

                    result = resultMap.AnalysisResult;

                    // record telemetry data
                    UpdateAnalyzerTelemetryData(resultMap.TelemetryInfo);
                }

                // check whether there is IDE specific project diagnostic analyzer
                return(await MergeProjectDiagnosticAnalyzerDiagnosticsAsync(project, ideAnalyzers, compilationWithAnalyzers?.Compilation, result, cancellationToken).ConfigureAwait(false));
            }
            catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Esempio n. 2
0
        private async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > ComputeDiagnosticsAsync(
            CompilationWithAnalyzers?compilationWithAnalyzers, Project project, IEnumerable <StateSet> stateSets, bool forcedAnalysis,
            ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> existing, CancellationToken cancellationToken)
        {
            try
            {
                // PERF: check whether we can reduce number of analyzers we need to run.
                //       this can happen since caller could have created the driver with different set of analyzers that are different
                //       than what we used to create the cache.
                var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);

                var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer || a is DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty();

                if (compilationWithAnalyzers != null && TryReduceAnalyzersToRun(compilationWithAnalyzers, project, version, existing, out var analyzersToRun))
                {
                    // it looks like we can reduce the set. create new CompilationWithAnalyzer.
                    // if we reduced to 0, we just pass in null for analyzer drvier. it could be reduced to 0
                    // since we might have up to date results for analyzers from compiler but not for
                    // workspace analyzers.
                    var compilationWithReducedAnalyzers = (analyzersToRun.Length == 0) ? null :
                                                          await AnalyzerHelper.CreateCompilationWithAnalyzersAsync(project, analyzersToRun, compilationWithAnalyzers.AnalysisOptions.ReportSuppressedDiagnostics, cancellationToken).ConfigureAwait(false);

                    var result = await ComputeDiagnosticsAsync(compilationWithReducedAnalyzers, project, ideAnalyzers, forcedAnalysis, cancellationToken).ConfigureAwait(false);

                    return(MergeExistingDiagnostics(version, existing, result));
                }

                // we couldn't reduce the set.
                return(await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, ideAnalyzers, forcedAnalysis, cancellationToken).ConfigureAwait(false));
            }
            catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Esempio n. 3
0
 private LatestDiagnosticsForSpanGetter(
     DiagnosticIncrementalAnalyzer owner,
     CompilationWithAnalyzers?compilationWithAnalyzers,
     Document document,
     IEnumerable <StateSet> stateSets,
     Func <string, bool>?shouldIncludeDiagnostic,
     bool includeCompilerDiagnostics,
     TextSpan?range,
     bool blockForData,
     Func <string, IDisposable?>?addOperationScope,
     bool includeSuppressedDiagnostics,
     CodeActionRequestPriority priority)
 {
     _owner = owner;
     _compilationWithAnalyzers = compilationWithAnalyzers;
     _document  = document;
     _stateSets = stateSets;
     _shouldIncludeDiagnostic    = shouldIncludeDiagnostic;
     _includeCompilerDiagnostics = includeCompilerDiagnostics;
     _range                        = range;
     _blockForData                 = blockForData;
     _addOperationScope            = addOperationScope;
     _includeSuppressedDiagnostics = includeSuppressedDiagnostics;
     _priority                     = priority;
 }
        private async Task ReportAnalyzerPerformanceAsync(Document document, CompilationWithAnalyzers?compilation, CancellationToken cancellationToken)
        {
            try
            {
                if (compilation == null)
                {
                    return;
                }

                var client = await RemoteHostClient.TryGetClientAsync(document.Project.Solution.Workspace, cancellationToken).ConfigureAwait(false);

                if (client == null)
                {
                    // no remote support
                    return;
                }

                cancellationToken.ThrowIfCancellationRequested();

                using var pooledObject = SharedPools.Default <Dictionary <DiagnosticAnalyzer, AnalyzerTelemetryInfo> >().GetPooledObject();

                var containsData = false;
                foreach (var analyzer in compilation.Analyzers)
                {
                    var telemetryInfo = await compilation.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken).ConfigureAwait(false);

                    if (!containsData && telemetryInfo.ExecutionTime.Ticks > 0)
                    {
                        // this is unfortunate tweak due to how GetAnalyzerTelemetryInfoAsync works when analyzers are asked
                        // one by one rather than in bulk.
                        containsData = true;
                    }

                    pooledObject.Object.Add(analyzer, telemetryInfo);
                }

                if (!containsData)
                {
                    // looks like there is no new data from driver. skip reporting.
                    return;
                }

                _ = await client.TryRunRemoteAsync(
                    WellKnownServiceHubService.CodeAnalysis,
                    nameof(IRemoteDiagnosticAnalyzerService.ReportAnalyzerPerformance),
                    solution : null,
                    new object[] { pooledObject.Object.ToAnalyzerPerformanceInfo(DiagnosticAnalyzerInfoCache), /* unit count */ 1 },
                    callbackTarget : null,
                    cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex) when(FatalError.ReportWithoutCrashUnlessCanceled(ex))
            {
                // this is fire and forget method
            }
        }
Esempio n. 5
0
        private void AssertAnalyzers(CompilationWithAnalyzers?compilation, IEnumerable <StateSet> stateSets)
        {
            if (compilation == null)
            {
                // this can happen if project doesn't support compilation or no stateSets are given.
                return;
            }

            // make sure analyzers are same.
            Contract.ThrowIfFalse(compilation.Analyzers.SetEquals(stateSets.Select(s => s.Analyzer).Where(a => !a.IsWorkspaceDiagnosticAnalyzer())));
        }
Esempio n. 6
0
        public DocumentAnalysisExecutor(
            DocumentAnalysisScope analysisScope,
            CompilationWithAnalyzers?compilationWithAnalyzers,
            DiagnosticAnalyzerInfoCache analyzerInfoCache)
        {
            AnalysisScope             = analysisScope;
            _compilationWithAnalyzers = compilationWithAnalyzers;
            _analyzerInfoCache        = analyzerInfoCache;

            var compilationBasedAnalyzers = compilationWithAnalyzers?.Analyzers.ToImmutableHashSet();

            _compilationBasedAnalyzersInAnalysisScope = compilationBasedAnalyzers != null
                ? analysisScope.Analyzers.WhereAsArray(compilationBasedAnalyzers.Contains)
                : ImmutableArray <DiagnosticAnalyzer> .Empty;
        }
Esempio n. 7
0
        private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, CancellationToken cancellationToken)
        {
            try
            {
                var stateSets = GetStateSetsForFullSolutionAnalysis(_stateManager.GetOrUpdateStateSets(project), project);

                // get driver only with active analyzers.
                var ideOptions = AnalyzerService.GlobalOptions.GetIdeAnalyzerOptions(project);

                // PERF: get analyzers that are not suppressed and marked as open file only
                // this is perf optimization. we cache these result since we know the result. (no diagnostics)
                var activeAnalyzers = stateSets
                                      .Select(s => s.Analyzer)
                                      .Where(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, project, GlobalOptions) && !a.IsOpenFileOnly(ideOptions.CleanupOptions?.SimplifierOptions));

                CompilationWithAnalyzers?compilationWithAnalyzers = null;

                if (forceAnalyzerRun || GlobalOptions.IsFullSolutionAnalysisEnabled(project.Language))
                {
                    compilationWithAnalyzers = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, ideOptions, activeAnalyzers, includeSuppressedDiagnostics : true, cancellationToken).ConfigureAwait(false);
                }

                var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, ideOptions, stateSets, forceAnalyzerRun, cancellationToken).ConfigureAwait(false);

                if (result.OldResult == null)
                {
                    RaiseProjectDiagnosticsIfNeeded(project, stateSets, result.Result);
                    return;
                }

                // no cancellation after this point.
                // any analyzer that doesn't have result will be treated as returned empty set
                // which means we will remove those from error list
                foreach (var stateSet in stateSets)
                {
                    var state = stateSet.GetOrCreateProjectState(project.Id);

                    await state.SaveToInMemoryStorageAsync(project, result.GetResult(stateSet.Analyzer)).ConfigureAwait(false);
                }

                RaiseProjectDiagnosticsIfNeeded(project, stateSets, result.OldResult, result.Result);
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Esempio n. 8
0
        public DocumentAnalysisExecutor(
            DocumentAnalysisScope analysisScope,
            CompilationWithAnalyzers?compilationWithAnalyzers,
            InProcOrRemoteHostAnalyzerRunner diagnosticAnalyzerRunner,
            bool logPerformanceInfo)
        {
            AnalysisScope             = analysisScope;
            _compilationWithAnalyzers = compilationWithAnalyzers;
            _diagnosticAnalyzerRunner = diagnosticAnalyzerRunner;
            _logPerformanceInfo       = logPerformanceInfo;

            var compilationBasedAnalyzers = compilationWithAnalyzers?.Analyzers.ToImmutableHashSet();

            _compilationBasedAnalyzersInAnalysisScope = compilationBasedAnalyzers != null
                ? analysisScope.Analyzers.WhereAsArray(compilationBasedAnalyzers.Contains)
                : ImmutableArray <DiagnosticAnalyzer> .Empty;
        }
Esempio n. 9
0
        private static bool CompilationHasOpenFileOnlyAnalyzers(CompilationWithAnalyzers?compilationWithAnalyzers, OptionSet options)
        {
            if (compilationWithAnalyzers == null)
            {
                return(false);
            }

            foreach (var analyzer in compilationWithAnalyzers.Analyzers)
            {
                if (analyzer.IsOpenFileOnly(options))
                {
                    return(true);
                }
            }

            return(false);
        }
Esempio n. 10
0
        private static bool CompilationHasOpenFileOnlyAnalyzers(CompilationWithAnalyzers?compilation, Workspace workspace)
        {
            if (compilation == null)
            {
                return(false);
            }

            foreach (var analyzer in compilation.Analyzers)
            {
                if (analyzer.IsOpenFileOnly(workspace))
                {
                    return(true);
                }
            }

            return(false);
        }
Esempio n. 11
0
        private async Task <ProjectAnalysisData> GetProjectAnalysisDataAsync(
            CompilationWithAnalyzers?compilationWithAnalyzers, Project project, IEnumerable <StateSet> stateSets, bool forceAnalyzerRun, CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, stateSets, cancellationToken))
            {
                try
                {
                    // PERF: We need to flip this to false when we do actual diffing.
                    var avoidLoadingData = true;
                    var version          = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);

                    var existingData = await ProjectAnalysisData.CreateAsync(PersistentStorageService, project, stateSets, avoidLoadingData, cancellationToken).ConfigureAwait(false);

                    // We can't return here if we have open file only analyzers since saved data for open file only analyzer
                    // is incomplete -- it only contains info on open files rather than whole project.
                    if (existingData.Version == version && !CompilationHasOpenFileOnlyAnalyzers(compilationWithAnalyzers, project.Solution.Options))
                    {
                        return(existingData);
                    }

                    // PERF: Check whether we want to analyze this project or not.
                    if (!FullAnalysisEnabled(project, forceAnalyzerRun))
                    {
                        Logger.Log(FunctionId.Diagnostics_ProjectDiagnostic, p => $"FSA off ({p.FilePath ?? p.Name})", project);

                        return(new ProjectAnalysisData(project.Id, VersionStamp.Default, existingData.Result, ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> .Empty));
                    }

                    var result = await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, stateSets, forceAnalyzerRun, existingData.Result, cancellationToken).ConfigureAwait(false);

                    // If project is not loaded successfully, get rid of any semantic errors from compiler analyzer.
                    // Note: In the past when project was not loaded successfully we did not run any analyzers on the project.
                    // Now we run analyzers but filter out some information. So on such projects, there will be some perf degradation.
                    result = await RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync(result, project, cancellationToken).ConfigureAwait(false);

                    return(new ProjectAnalysisData(project.Id, version, existingData.Result, result));
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
        }
        private async Task <DocumentAnalysisData> GetDocumentAnalysisDataAsync(
            CompilationWithAnalyzers?compilation, Document document, StateSet stateSet, AnalysisKind kind, CancellationToken cancellationToken)
        {
            // get log title and functionId
            GetLogFunctionIdAndTitle(kind, out var functionId, out var title);

            using (Logger.LogBlock(functionId, GetDocumentLogMessage, title, document, stateSet.Analyzer, cancellationToken))
            {
                try
                {
                    var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false);

                    var state        = stateSet.GetOrCreateActiveFileState(document.Id);
                    var existingData = state.GetAnalysisData(kind);

                    if (existingData.Version == version)
                    {
                        return(existingData);
                    }

                    // perf optimization. check whether analyzer is suppressed and avoid getting diagnostics if suppressed.
                    // REVIEW: IsAnalyzerSuppressed call seems can be quite expensive in certain condition. is there any other way to do this?
                    if (AnalyzerService.IsAnalyzerSuppressed(stateSet.Analyzer, document.Project))
                    {
                        return(new DocumentAnalysisData(version, existingData.Items, ImmutableArray <DiagnosticData> .Empty));
                    }

                    var nullFilterSpan = (TextSpan?)null;
                    var diagnostics    = await ComputeDiagnosticsAsync(compilation, document, stateSet.Analyzer, kind, nullFilterSpan, cancellationToken).ConfigureAwait(false);

                    // this is no-op in product. only run in test environment
                    Logger.Log(functionId, (t, d, a, ds) => $"{GetDocumentLogMessage(t, d, a)}, {string.Join(Environment.NewLine, ds)}",
                               title, document, stateSet.Analyzer, diagnostics);

                    // we only care about local diagnostics
                    return(new DocumentAnalysisData(version, existingData.Items, diagnostics.ToImmutableArrayOrEmpty()));
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
        }
 private LatestDiagnosticsForSpanGetter(
     DiagnosticIncrementalAnalyzer owner,
     CompilationWithAnalyzers?compilationWithAnalyzers,
     Document document,
     IEnumerable <StateSet> stateSets,
     string?diagnosticId,
     TextSpan range,
     bool blockForData,
     Func <string, IDisposable?>?addOperationScope,
     bool includeSuppressedDiagnostics)
 {
     _owner = owner;
     _compilationWithAnalyzers = compilationWithAnalyzers;
     _document                     = document;
     _stateSets                    = stateSets;
     _diagnosticId                 = diagnosticId;
     _range                        = range;
     _blockForData                 = blockForData;
     _addOperationScope            = addOperationScope;
     _includeSuppressedDiagnostics = includeSuppressedDiagnostics;
 }
            private LatestDiagnosticsForSpanGetter(
                DiagnosticIncrementalAnalyzer owner,
                CompilationWithAnalyzers?compilation,
                Document document,
                IEnumerable <StateSet> stateSets,
                string?diagnosticId,
                TextSpan range, bool blockForData, bool includeSuppressedDiagnostics)
            {
                _owner = owner;

                _project  = document.Project;
                _document = document;

                _stateSets    = stateSets;
                _diagnosticId = diagnosticId;
                _compilation  = compilation;

                _range        = range;
                _blockForData = blockForData;
                _includeSuppressedDiagnostics = includeSuppressedDiagnostics;
            }
 private Task <IEnumerable <DiagnosticData> > ComputeDiagnosticsAsync(
     CompilationWithAnalyzers?compilation, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan?spanOpt, CancellationToken cancellationToken)
 {
     return(AnalyzerService.ComputeDiagnosticsAsync(compilation, document, analyzer, kind, spanOpt, DiagnosticLogAggregator, cancellationToken));
 }
Esempio n. 16
0
 public ProjectAndCompilationWithAnalyzers(Project project, CompilationWithAnalyzers?compilationWithAnalyzers)
 {
     Project = project;
     CompilationWithAnalyzers = compilationWithAnalyzers;
 }