/// <summary> /// Return all local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer) either from cache or by calculating them /// </summary> public async Task<DocumentAnalysisData> GetDocumentAnalysisDataAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, StateSet stateSet, AnalysisKind kind, CancellationToken cancellationToken) { try { var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); var state = stateSet.GetActiveFileState(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 (_owner.Owner.IsAnalyzerSuppressed(stateSet.Analyzer, document.Project)) { return new DocumentAnalysisData(version, existingData.Items, ImmutableArray<DiagnosticData>.Empty); } var nullFilterSpan = (TextSpan?)null; var diagnostics = await ComputeDiagnosticsAsync(analyzerDriverOpt, document, stateSet.Analyzer, kind, nullFilterSpan, cancellationToken).ConfigureAwait(false); // we only care about local diagnostics return new DocumentAnalysisData(version, existingData.Items, diagnostics.ToImmutableArrayOrEmpty()); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
public DocumentAnalysisData GetAnalysisData(AnalysisKind kind) { switch (kind) { case AnalysisKind.Syntax: return _syntax; case AnalysisKind.Semantic: return _semantic; default: return Contract.FailWithReturn<DocumentAnalysisData>("Shouldn't reach here"); } }
private bool SupportAnalysisKind(DiagnosticAnalyzer analyzer, string language, AnalysisKind kind) { // compiler diagnostic analyzer always support all kinds if (HostAnalyzerManager.IsCompilerDiagnosticAnalyzer(language, analyzer)) { return true; } switch (kind) { case AnalysisKind.Syntax: return analyzer.SupportsSyntaxDiagnosticAnalysis(); case AnalysisKind.Semantic: return analyzer.SupportsSemanticDiagnosticAnalysis(); default: return Contract.FailWithReturn<bool>("shouldn't reach here"); } }
public void Save(AnalysisKind kind, DocumentAnalysisData data) { Contract.ThrowIfFalse(data.OldItems.IsDefault); switch (kind) { case AnalysisKind.Syntax: _syntax = data; return; case AnalysisKind.Semantic: _semantic = data; return; default: Contract.Fail("Shouldn't reach here"); return; } }
private async Task AnalyzeDocumentForKindAsync(Document document, AnalysisKind kind, CancellationToken cancellationToken) { try { if (!AnalysisEnabled(document)) { // to reduce allocations, here, we don't clear existing diagnostics since it is dealt by other entry point such as // DocumentReset or DocumentClosed. return; } var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); var analyzerDriverOpt = await _compilationManager.GetAnalyzerDriverAsync(document.Project, stateSets, cancellationToken).ConfigureAwait(false); foreach (var stateSet in stateSets) { var analyzer = stateSet.Analyzer; var result = await _executor.GetDocumentAnalysisDataAsync(analyzerDriverOpt, document, stateSet, kind, cancellationToken).ConfigureAwait(false); if (result.FromCache) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, result.Items); continue; } // no cancellation after this point. var state = stateSet.GetActiveFileState(document.Id); state.Save(kind, result.ToPersistData()); RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, result.OldItems, result.Items); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
/// <summary> /// Return all local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer) by calculating them /// </summary> public async Task<IEnumerable<DiagnosticData>> ComputeDiagnosticsAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? spanOpt, CancellationToken cancellationToken) { var documentAnalyzer = analyzer as DocumentDiagnosticAnalyzer; if (documentAnalyzer != null) { var diagnostics = await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, kind, analyzerDriverOpt?.Compilation, cancellationToken).ConfigureAwait(false); return ConvertToLocalDiagnostics(document, diagnostics); } var documentDiagnostics = await ComputeDiagnosticAnalyzerDiagnosticsAsync(analyzerDriverOpt, document, analyzer, kind, spanOpt, cancellationToken).ConfigureAwait(false); return ConvertToLocalDiagnostics(document, documentDiagnostics); }
private async Task<IEnumerable<Diagnostic>> ComputeDiagnosticAnalyzerDiagnosticsAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? spanOpt, CancellationToken cancellationToken) { // quick optimization to reduce allocations. if (analyzerDriverOpt == null || !_owner.SupportAnalysisKind(analyzer, document.Project.Language, kind)) { return ImmutableArray<Diagnostic>.Empty; } // REVIEW: more unnecessary allocations just to get diagnostics per analyzer var oneAnalyzers = ImmutableArray.Create(analyzer); switch (kind) { case AnalysisKind.Syntax: var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var diagnostics = await analyzerDriverOpt.GetAnalyzerSyntaxDiagnosticsAsync(tree, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return diagnostics.ToImmutableArrayOrEmpty(); case AnalysisKind.Semantic: var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); diagnostics = await analyzerDriverOpt.GetAnalyzerSemanticDiagnosticsAsync(model, spanOpt, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return diagnostics.ToImmutableArrayOrEmpty(); default: return Contract.FailWithReturn<ImmutableArray<Diagnostic>>("shouldn't reach here"); } }
protected override async Task <ImmutableArray <DiagnosticData> > GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId?documentId, AnalysisKind kind, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var stateSets = SpecializedCollections.SingletonCollection(stateSet); // Here, we don't care what kind of analyzer (StateSet) is given. var forceAnalyzerRun = true; var compilation = await Owner.CreateCompilationWithAnalyzersAsync(project, stateSets, IncludeSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); if (documentId != null) { var document = project.Solution.GetDocument(documentId); Contract.ThrowIfNull(document); switch (kind) { case AnalysisKind.Syntax: case AnalysisKind.Semantic: return((await Owner.GetDocumentAnalysisDataAsync(compilation, document, stateSet, kind, cancellationToken).ConfigureAwait(false)).Items); case AnalysisKind.NonLocal: var nonLocalDocumentResult = await Owner.GetProjectAnalysisDataAsync(compilation, project, stateSets, forceAnalyzerRun, cancellationToken).ConfigureAwait(false); var analysisResult = nonLocalDocumentResult.GetResult(stateSet.Analyzer); return(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); default: throw ExceptionUtilities.UnexpectedValue(kind); } } Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); var projectResult = await Owner.GetProjectAnalysisDataAsync(compilation, project, stateSets, forceAnalyzerRun, cancellationToken).ConfigureAwait(false); return(projectResult.GetResult(stateSet.Analyzer).GetOtherDiagnostics()); }
public async Task <ImmutableArray <DiagnosticData> > GetSpecificDiagnosticsAsync(DiagnosticAnalyzer analyzer, AnalysisKind analysisKind, CancellationToken cancellationToken) { var project = Solution.GetProject(ProjectId); if (project == null) { // when we return cached result, make sure we at least return something that exist in current solution return(ImmutableArray <DiagnosticData> .Empty); } var stateSet = StateManager.GetOrCreateStateSet(project, analyzer); if (stateSet == null) { return(ImmutableArray <DiagnosticData> .Empty); } var diagnostics = await GetDiagnosticsAsync(stateSet, project, DocumentId, analysisKind, cancellationToken).ConfigureAwait(false); return(IncludeSuppressedDiagnostics ? diagnostics : diagnostics.WhereAsArray(d => !d.IsSuppressed)); }
private async Task<ImmutableArray<DiagnosticData>?> GetProjectStateDiagnosticsAsync( StateSet stateSet, Project project, DocumentId documentId, AnalysisKind kind, CancellationToken cancellationToken) { if (!stateSet.TryGetProjectState(project.Id, out var state)) { // never analyzed this project yet. return null; } if (documentId != null) { // file doesn't exist in current solution var document = project.Solution.GetDocument(documentId); if (document == null) { return null; } var result = await state.GetAnalysisDataAsync(document, avoidLoadingData: false, cancellationToken: cancellationToken).ConfigureAwait(false); return GetResult(result, kind, documentId); } Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); var nonLocalResult = await state.GetProjectAnalysisDataAsync(project, avoidLoadingData: false, cancellationToken: cancellationToken).ConfigureAwait(false); return nonLocalResult.Others; }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, ImmutableArray <DiagnosticData> oldItems, ImmutableArray <DiagnosticData> newItems) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, AnalyzerService.RaiseDiagnosticsUpdated, forceUpdate: false); }
/// <summary> /// Return all local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer) either from cache or by calculating them /// </summary> public async Task <DocumentAnalysisData> GetDocumentAnalysisDataAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, StateSet stateSet, AnalysisKind kind, CancellationToken cancellationToken) { try { var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); var state = stateSet.GetActiveFileState(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 (_owner.Owner.IsAnalyzerSuppressed(stateSet.Analyzer, document.Project)) { return(new DocumentAnalysisData(version, existingData.Items, ImmutableArray <DiagnosticData> .Empty)); } var nullFilterSpan = (TextSpan?)null; var diagnostics = await ComputeDiagnosticsAsync(analyzerDriverOpt, document, stateSet.Analyzer, kind, nullFilterSpan, cancellationToken).ConfigureAwait(false); // 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 static ImmutableArray<DiagnosticData> GetResult(DiagnosticAnalysisResult result, AnalysisKind kind, DocumentId id) { if (result.IsEmpty || !result.DocumentIds.Contains(id) || result.IsAggregatedForm) { return ImmutableArray<DiagnosticData>.Empty; } switch (kind) { case AnalysisKind.Syntax: return result.GetResultOrEmpty(result.SyntaxLocals, id); case AnalysisKind.Semantic: return result.GetResultOrEmpty(result.SemanticLocals, id); case AnalysisKind.NonLocal: return result.GetResultOrEmpty(result.NonLocals, id); default: return Contract.FailWithReturn<ImmutableArray<DiagnosticData>>("shouldn't reach here"); } }
/// <summary> /// Return all local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer) by calculating them /// </summary> public async Task <IEnumerable <DiagnosticData> > ComputeDiagnosticsAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan?spanOpt, CancellationToken cancellationToken) { var documentAnalyzer = analyzer as DocumentDiagnosticAnalyzer; if (documentAnalyzer != null) { var diagnostics = await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, kind, analyzerDriverOpt?.Compilation, cancellationToken).ConfigureAwait(false); return(ConvertToLocalDiagnostics(document, diagnostics)); } var documentDiagnostics = await ComputeDiagnosticAnalyzerDiagnosticsAsync(analyzerDriverOpt, document, analyzer, kind, spanOpt, cancellationToken).ConfigureAwait(false); return(ConvertToLocalDiagnostics(document, documentDiagnostics)); }
private async Task <IEnumerable <Diagnostic> > ComputeDiagnosticAnalyzerDiagnosticsAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan?spanOpt, CancellationToken cancellationToken) { // quick optimization to reduce allocations. if (analyzerDriverOpt == null || !_owner.SupportAnalysisKind(analyzer, document.Project.Language, kind)) { return(ImmutableArray <Diagnostic> .Empty); } // REVIEW: more unnecessary allocations just to get diagnostics per analyzer var oneAnalyzers = ImmutableArray.Create(analyzer); switch (kind) { case AnalysisKind.Syntax: var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var diagnostics = await analyzerDriverOpt.GetAnalyzerSyntaxDiagnosticsAsync(tree, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return(diagnostics.ToImmutableArrayOrEmpty()); case AnalysisKind.Semantic: var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); diagnostics = await analyzerDriverOpt.GetAnalyzerSemanticDiagnosticsAsync(model, spanOpt, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return(diagnostics.ToImmutableArrayOrEmpty()); default: return(Contract.FailWithReturn <ImmutableArray <Diagnostic> >("shouldn't reach here")); } }
private static object CreateId(StateSet stateSet, ProjectId projectId, AnalysisKind kind) => new LiveDiagnosticUpdateArgsId(stateSet.Analyzer, projectId, (int)kind, stateSet.ErrorSourceName);
private static async Task <ImmutableArray <DiagnosticData> > GetProjectStateDiagnosticsAsync(StateSet stateSet, Project project, DocumentId?documentId, AnalysisKind kind, CancellationToken cancellationToken) { if (!stateSet.TryGetProjectState(project.Id, out var state)) { // never analyzed this project yet. return(ImmutableArray <DiagnosticData> .Empty); } if (documentId != null) { // file doesn't exist in current solution var document = await project.Solution.GetDocumentAsync( documentId, includeSourceGenerated : project.Solution.Workspace.Services.GetService <ISyntaxTreeConfigurationService>() is { EnableOpeningSourceGeneratedFilesInWorkspace : true },
public AggregatedKey(ImmutableArray <DocumentId> documentIds, DiagnosticAnalyzer analyzer, AnalysisKind kind) { DocumentIds = documentIds; Analyzer = analyzer; Kind = kind; }
private void RaiseDiagnosticsCreated( Document document, StateSet stateSet, AnalysisKind kind, ImmutableArray<DiagnosticData> items, Action<DiagnosticsUpdatedArgs> raiseEvents) { Contract.ThrowIfFalse(document.Project.Solution.Workspace == Workspace); raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsCreated( CreateId(stateSet.Analyzer, document.Id, kind, stateSet.ErrorSourceName), document.Project.Solution.Workspace, document.Project.Solution, document.Project.Id, document.Id, items)); }
private void RaiseDocumentDiagnosticsIfNeeded(Document document, StateSet stateSet, AnalysisKind kind, ImmutableArray <DiagnosticData> items) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, ImmutableArray <DiagnosticData> .Empty, items); }
private object CreateId(DiagnosticAnalyzer analyzer, ProjectId key, AnalysisKind kind, string errorSourceName) { return CreateIdInternal(analyzer, key, kind, errorSourceName); }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, ImmutableArray <DiagnosticData> oldItems, ImmutableArray <DiagnosticData> newItems) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, Owner.RaiseDiagnosticsUpdated); }
protected override async Task<ImmutableArray<DiagnosticData>?> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId documentId, AnalysisKind kind, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var activeFileDiagnostics = GetActiveFileDiagnostics(stateSet, documentId, kind); if (activeFileDiagnostics.HasValue) { return activeFileDiagnostics.Value; } var projectDiagnostics = await GetProjectStateDiagnosticsAsync(stateSet, project, documentId, kind, cancellationToken).ConfigureAwait(false); if (projectDiagnostics.HasValue) { return projectDiagnostics.Value; } return null; }
private ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> >?GetMap( AnalysisKind kind ) => kind switch {
protected abstract Task<ImmutableArray<DiagnosticData>?> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId documentId, AnalysisKind kind, CancellationToken cancellationToken);
public bool SupportAnalysisKind(DiagnosticAnalyzer analyzer, string language, AnalysisKind kind) { // compiler diagnostic analyzer always supports all kinds: if (IsCompilerDiagnosticAnalyzer(language, analyzer)) { return(true); } return(kind switch { AnalysisKind.Syntax => analyzer.SupportsSyntaxDiagnosticAnalysis(), AnalysisKind.Semantic => analyzer.SupportsSemanticDiagnosticAnalysis(), _ => throw ExceptionUtilities.UnexpectedValue(kind) });
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. if (AnalyzerService.IsAnalyzerSuppressed(stateSet.Analyzer, document.Project)) { return(new DocumentAnalysisData(version, existingData.Items, ImmutableArray <DiagnosticData> .Empty)); } var diagnostics = await AnalyzerService.ComputeDiagnosticsAsync(compilation, document, stateSet.Analyzer, kind, span : null, DiagnosticLogAggregator, 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 void RaiseEmptyDiagnosticUpdated(AnalysisKind kind, DocumentId documentId) { _service.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs.DiagnosticsRemoved( new DefaultUpdateArgsId(_workspace.Kind, kind, documentId), _workspace, null, documentId.ProjectId, documentId)); }
protected abstract Task <ImmutableArray <DiagnosticData> > GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId?documentId, AnalysisKind kind, CancellationToken cancellationToken);
public DefaultUpdateArgsId(string workspaceKind, AnalysisKind kind, DocumentId documentId) : base((int)kind, documentId) => _workspaceKind = workspaceKind;
protected override async Task <ImmutableArray <DiagnosticData>?> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId documentId, AnalysisKind kind, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var activeFileDiagnostics = GetActiveFileDiagnostics(stateSet, documentId, kind); if (activeFileDiagnostics.HasValue) { return(activeFileDiagnostics.Value); } var projectDiagnostics = await GetProjectStateDiagnosticsAsync(stateSet, project, documentId, kind, cancellationToken).ConfigureAwait(false); if (projectDiagnostics.HasValue) { return(projectDiagnostics.Value); } return(null); }
private ImmutableArray <DiagnosticData>?GetActiveFileDiagnostics(StateSet stateSet, DocumentId documentId, AnalysisKind kind) { if (documentId == null || kind == AnalysisKind.NonLocal) { return(null); } if (!stateSet.TryGetActiveFileState(documentId, out var state)) { return(null); } return(state.GetAnalysisData(kind).Items); }
private async Task<IEnumerable<Diagnostic>> ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync( Document document, DocumentDiagnosticAnalyzer analyzer, AnalysisKind kind, Compilation compilationOpt, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); try { Task<ImmutableArray<Diagnostic>> analyzeAsync; switch (kind) { case AnalysisKind.Syntax: analyzeAsync = analyzer.AnalyzeSyntaxAsync(document, cancellationToken); break; case AnalysisKind.Semantic: analyzeAsync = analyzer.AnalyzeSemanticsAsync(document, cancellationToken); break; default: throw ExceptionUtilities.UnexpectedValue(kind); } var diagnostics = (await analyzeAsync.ConfigureAwait(false)).NullToEmpty(); if (compilationOpt != null) { return CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt); } return diagnostics; } catch (Exception e) when (!IsCanceled(e, cancellationToken)) { OnAnalyzerException(analyzer, document.Project.Id, compilationOpt, e); return ImmutableArray<Diagnostic>.Empty; } }
protected override async Task <ImmutableArray <DiagnosticData>?> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId documentId, AnalysisKind kind, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var stateSets = SpecializedCollections.SingletonCollection(stateSet); // Here, we don't care what kind of analyzer (StateSet) is given. // We just create and use AnalyzerDriver with the given analyzer (StateSet). var forceAnalyzerRun = true; var analyzerDriverOpt = await Owner._compilationManager.CreateAnalyzerDriverAsync(project, stateSets, IncludeSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); if (documentId != null) { var document = project.Solution.GetDocument(documentId); Contract.ThrowIfNull(document); switch (kind) { case AnalysisKind.Syntax: case AnalysisKind.Semantic: { var result = await Owner._executor.GetDocumentAnalysisDataAsync(analyzerDriverOpt, document, stateSet, kind, cancellationToken).ConfigureAwait(false); return(result.Items); } case AnalysisKind.NonLocal: { var nonLocalDocumentResult = await Owner._executor.GetProjectAnalysisDataAsync(analyzerDriverOpt, project, stateSets, forceAnalyzerRun, cancellationToken).ConfigureAwait(false); var analysisResult = nonLocalDocumentResult.GetResult(stateSet.Analyzer); return(GetResult(analysisResult, AnalysisKind.NonLocal, documentId)); } default: return(Contract.FailWithReturn <ImmutableArray <DiagnosticData>?>("shouldn't reach here")); } } Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); var projectResult = await Owner._executor.GetProjectAnalysisDataAsync(analyzerDriverOpt, project, stateSets, forceAnalyzerRun, cancellationToken).ConfigureAwait(false); return(projectResult.GetResult(stateSet.Analyzer).Others); }
private static void GetLogFunctionIdAndTitle(AnalysisKind kind, out FunctionId functionId, out string title) { switch (kind) { case AnalysisKind.Syntax: functionId = FunctionId.Diagnostics_SyntaxDiagnostic; title = "syntax"; break; case AnalysisKind.Semantic: functionId = FunctionId.Diagnostics_SemanticDiagnostic; title = "semantic"; break; default: functionId = FunctionId.Diagnostics_ProjectDiagnostic; title = "nonLocal"; Contract.Fail("shouldn't reach here"); break; } }
private async Task AnalyzeDocumentForKindAsync( TextDocument document, AnalysisKind kind, CancellationToken cancellationToken ) { try { if (!AnalysisEnabled(document)) { // to reduce allocations, here, we don't clear existing diagnostics since it is dealt by other entry point such as // DocumentReset or DocumentClosed. return; } var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( document.Project, stateSets, cancellationToken ) .ConfigureAwait(false); // We split the diagnostic computation for document into following steps: // 1. Try to get cached diagnostics for each analyzer, while computing the set of analyzers that do not have cached diagnostics. // 2. Execute all the non-cached analyzers with a single invocation into CompilationWithAnalyzers. // 3. Fetch computed diagnostics per-analyzer from the above invocation, and cache and raise diagnostic reported events. // In near future, the diagnostic computation invocation into CompilationWithAnalyzers will be moved to OOP. // This should help simplify and/or remove the IDE layer diagnostic caching in devenv process. // First attempt to fetch diagnostics from the cache, while computing the state sets for analyzers that are not cached. using var _ = ArrayBuilder <StateSet> .GetInstance(out var nonCachedStateSets); foreach (var stateSet in stateSets) { var data = await TryGetCachedDocumentAnalysisDataAsync( document, stateSet, kind, cancellationToken ) .ConfigureAwait(false); if (data.HasValue) { // We need to persist and raise diagnostics for suppressed analyzer. PersistAndRaiseDiagnosticsIfNeeded(data.Value, stateSet); } else { nonCachedStateSets.Add(stateSet); } } // Then, compute the diagnostics for non-cached state sets, and cache and raise diagnostic reported events for these diagnostics. if (nonCachedStateSets.Count > 0) { var analysisScope = new DocumentAnalysisScope( document, span: null, nonCachedStateSets.SelectAsArray(s => s.Analyzer), kind ); var executor = new DocumentAnalysisExecutor( analysisScope, compilationWithAnalyzers, _diagnosticAnalyzerRunner, logPerformanceInfo: true, onAnalysisException: OnAnalysisException ); foreach (var stateSet in nonCachedStateSets) { var computedData = await ComputeDocumentAnalysisDataAsync( executor, stateSet, cancellationToken ) .ConfigureAwait(false); PersistAndRaiseDiagnosticsIfNeeded(computedData, stateSet); } } } catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { throw ExceptionUtilities.Unreachable; } void PersistAndRaiseDiagnosticsIfNeeded(DocumentAnalysisData result, StateSet stateSet) { if (result.FromCache == true) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, result.Items); return; } // no cancellation after this point. var state = stateSet.GetOrCreateActiveFileState(document.Id); state.Save(kind, result.ToPersistData()); RaiseDocumentDiagnosticsIfNeeded( document, stateSet, kind, result.OldItems, result.Items ); } void OnAnalysisException() { // Do not re-use cached CompilationWithAnalyzers instance in presence of an exception, as the underlying analysis state might be corrupt. ClearCompilationsWithAnalyzersCache(document.Project); } }
private void RaiseDiagnosticsRemoved( DocumentId documentId, Solution solution, StateSet stateSet, AnalysisKind kind, Action<DiagnosticsUpdatedArgs> raiseEvents) { Contract.ThrowIfFalse(solution == null || solution.Workspace == Workspace); raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsRemoved( CreateId(stateSet.Analyzer, documentId, kind, stateSet.ErrorSourceName), Workspace, solution, documentId.ProjectId, documentId)); }
private object CreateId(DiagnosticAnalyzer analyzer, ProjectId key, AnalysisKind kind, string errorSourceName) { return(CreateIdInternal(analyzer, key, kind, errorSourceName)); }
private static object CreateIdInternal(DiagnosticAnalyzer analyzer, object key, AnalysisKind kind, string errorSourceName) { return new LiveDiagnosticUpdateArgsId(analyzer, key, (int)kind, errorSourceName); }
private static object CreateIdInternal(DiagnosticAnalyzer analyzer, object key, AnalysisKind kind, string errorSourceName) { return(new LiveDiagnosticUpdateArgsId(analyzer, key, (int)kind, errorSourceName)); }
public DocumentAnalysisData GetAnalysisData(AnalysisKind kind) => kind switch {
private static ImmutableArray <DiagnosticData> GetResult(DiagnosticAnalysisResult result, AnalysisKind kind, DocumentId id) { if (result.IsEmpty || !result.DocumentIds.Contains(id) || result.IsAggregatedForm) { return(ImmutableArray <DiagnosticData> .Empty); } return(kind switch { AnalysisKind.Syntax => result.GetResultOrEmpty(result.SyntaxLocals, id), AnalysisKind.Semantic => result.GetResultOrEmpty(result.SemanticLocals, id), AnalysisKind.NonLocal => result.GetResultOrEmpty(result.NonLocals, id), _ => Contract.FailWithReturn <ImmutableArray <DiagnosticData> >("shouldn't reach here"), });
private ImmutableArray<DiagnosticData>? GetActiveFileDiagnostics(StateSet stateSet, DocumentId documentId, AnalysisKind kind) { if (documentId == null || kind == AnalysisKind.NonLocal) { return null; } if (!stateSet.TryGetActiveFileState(documentId, out var state)) { return null; } return state.GetAnalysisData(kind).Items; }
private void RaiseDocumentDiagnosticsIfNeeded(Document document, StateSet stateSet, AnalysisKind kind, ImmutableArray<DiagnosticData> items) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, ImmutableArray<DiagnosticData>.Empty, items); }
protected override async Task<ImmutableArray<DiagnosticData>?> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId documentId, AnalysisKind kind, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var stateSets = SpecializedCollections.SingletonCollection(stateSet); // Here, we don't care what kind of analyzer (StateSet) is given. // We just create and use AnalyzerDriver with the given analyzer (StateSet). var ignoreFullAnalysisOptions = true; var analyzerDriverOpt = await Owner._compilationManager.CreateAnalyzerDriverAsync(project, stateSets, IncludeSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); if (documentId != null) { var document = project.Solution.GetDocument(documentId); Contract.ThrowIfNull(document); switch (kind) { case AnalysisKind.Syntax: case AnalysisKind.Semantic: { var result = await Owner._executor.GetDocumentAnalysisDataAsync(analyzerDriverOpt, document, stateSet, kind, cancellationToken).ConfigureAwait(false); return result.Items; } case AnalysisKind.NonLocal: { var nonLocalDocumentResult = await Owner._executor.GetProjectAnalysisDataAsync(analyzerDriverOpt, project, stateSets, ignoreFullAnalysisOptions, cancellationToken).ConfigureAwait(false); var analysisResult = nonLocalDocumentResult.GetResult(stateSet.Analyzer); return GetResult(analysisResult, AnalysisKind.NonLocal, documentId); } default: return Contract.FailWithReturn<ImmutableArray<DiagnosticData>?>("shouldn't reach here"); } } Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); var projectResult = await Owner._executor.GetProjectAnalysisDataAsync(analyzerDriverOpt, project, stateSets, ignoreFullAnalysisOptions, cancellationToken).ConfigureAwait(false); return projectResult.GetResult(stateSet.Analyzer).Others; }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, DiagnosticAnalysisResult oldResult, DiagnosticAnalysisResult newResult, Action<DiagnosticsUpdatedArgs> raiseEvents) { var oldItems = GetResult(oldResult, kind, document.Id); var newItems = GetResult(newResult, kind, document.Id); RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, raiseEvents); }
private static ImmutableArray <DiagnosticData> GetResult(DiagnosticAnalysisResult result, AnalysisKind kind, DocumentId id) { if (result.IsEmpty || !result.DocumentIds.Contains(id) || result.IsAggregatedForm) { return(ImmutableArray <DiagnosticData> .Empty); } switch (kind) { case AnalysisKind.Syntax: return(result.GetResultOrEmpty(result.SyntaxLocals, id)); case AnalysisKind.Semantic: return(result.GetResultOrEmpty(result.SemanticLocals, id)); case AnalysisKind.NonLocal: return(result.GetResultOrEmpty(result.NonLocals, id)); default: return(Contract.FailWithReturn <ImmutableArray <DiagnosticData> >("shouldn't reach here")); } }
private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKind kind, CancellationToken cancellationToken) { try { if (!document.SupportsDiagnostics()) { return; } var isActiveDocument = _documentTrackingService.TryGetActiveDocument() == document.Id; var isOpenDocument = document.IsOpen(); var isGeneratedRazorDocument = document.IsRazorDocument(); // Only analyze open/active documents, unless it is a generated Razor document. if (!isActiveDocument && !isOpenDocument && !isGeneratedRazorDocument) { return; } var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync(document.Project, stateSets, cancellationToken).ConfigureAwait(false); var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); var backgroundAnalysisScope = GlobalOptions.GetBackgroundAnalysisScope(document.Project.Language); var compilerDiagnosticsScope = GlobalOptions.GetOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, document.Project.Language); // TODO: Switch to a more reliable service to determine visible documents. // DocumentTrackingService is known be unreliable at times. var isVisibleDocument = _documentTrackingService.GetVisibleDocuments().Contains(document.Id); // We split the diagnostic computation for document into following steps: // 1. Try to get cached diagnostics for each analyzer, while computing the set of analyzers that do not have cached diagnostics. // 2. Execute all the non-cached analyzers with a single invocation into CompilationWithAnalyzers. // 3. Fetch computed diagnostics per-analyzer from the above invocation, and cache and raise diagnostic reported events. // In near future, the diagnostic computation invocation into CompilationWithAnalyzers will be moved to OOP. // This should help simplify and/or remove the IDE layer diagnostic caching in devenv process. // First attempt to fetch diagnostics from the cache, while computing the state sets for analyzers that are not cached. using var _ = ArrayBuilder <StateSet> .GetInstance(out var nonCachedStateSets); foreach (var stateSet in stateSets) { var data = TryGetCachedDocumentAnalysisData(document, stateSet, kind, version, backgroundAnalysisScope, compilerDiagnosticsScope, isActiveDocument, isVisibleDocument, isOpenDocument, isGeneratedRazorDocument, cancellationToken); if (data.HasValue) { // We need to persist and raise diagnostics for suppressed analyzer. PersistAndRaiseDiagnosticsIfNeeded(data.Value, stateSet); } else { nonCachedStateSets.Add(stateSet); } } // Then, compute the diagnostics for non-cached state sets, and cache and raise diagnostic reported events for these diagnostics. if (nonCachedStateSets.Count > 0) { var analysisScope = new DocumentAnalysisScope(document, span: null, nonCachedStateSets.SelectAsArray(s => s.Analyzer), kind); var executor = new DocumentAnalysisExecutor(analysisScope, compilationWithAnalyzers, _diagnosticAnalyzerRunner, logPerformanceInfo: true, onAnalysisException: OnAnalysisException); var logTelemetry = GlobalOptions.GetOption(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); foreach (var stateSet in nonCachedStateSets) { var computedData = await ComputeDocumentAnalysisDataAsync(executor, stateSet, logTelemetry, cancellationToken).ConfigureAwait(false); PersistAndRaiseDiagnosticsIfNeeded(computedData, stateSet); } } } catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { throw ExceptionUtilities.Unreachable; } void PersistAndRaiseDiagnosticsIfNeeded(DocumentAnalysisData result, StateSet stateSet) { if (result.FromCache == true) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, result.Items); return; } // no cancellation after this point. var state = stateSet.GetOrCreateActiveFileState(document.Id); state.Save(kind, result.ToPersistData()); RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, result.OldItems, result.Items); } void OnAnalysisException() { // Do not re-use cached CompilationWithAnalyzers instance in presence of an exception, as the underlying analysis state might be corrupt. ClearCompilationsWithAnalyzersCache(document.Project); } }
private bool SupportAnalysisKind(DiagnosticAnalyzer analyzer, string language, AnalysisKind kind) { // compiler diagnostic analyzer always support all kinds if (HostAnalyzerManager.IsCompilerDiagnosticAnalyzer(language, analyzer)) { return(true); } switch (kind) { case AnalysisKind.Syntax: return(analyzer.SupportsSyntaxDiagnosticAnalysis()); case AnalysisKind.Semantic: return(analyzer.SupportsSemanticDiagnosticAnalysis()); default: return(Contract.FailWithReturn <bool>("shouldn't reach here")); } }
protected override async Task <ImmutableArray <DiagnosticData> > GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId?documentId, AnalysisKind kind, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // active file diagnostics: if (documentId != null && kind != AnalysisKind.NonLocal && stateSet.TryGetActiveFileState(documentId, out var state)) { return(state.GetAnalysisData(kind).Items); } // project diagnostics: return(await GetProjectStateDiagnosticsAsync(stateSet, project, documentId, kind, cancellationToken).ConfigureAwait(false)); }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, ImmutableArray<DiagnosticData> oldItems, ImmutableArray<DiagnosticData> newItems) { RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, Owner.RaiseDiagnosticsUpdated); }
private async Task <ImmutableArray <DiagnosticData> > GetProjectStateDiagnosticsAsync(StateSet stateSet, Project project, DocumentId?documentId, AnalysisKind kind, CancellationToken cancellationToken) { if (!stateSet.TryGetProjectState(project.Id, out var state)) { // never analyzed this project yet. return(ImmutableArray <DiagnosticData> .Empty); } if (documentId != null) { // file doesn't exist in current solution var document = project.Solution.GetDocument(documentId); if (document == null) { return(ImmutableArray <DiagnosticData> .Empty); } var result = await state.GetAnalysisDataAsync(Owner.PersistentStorageService, document, avoidLoadingData : false, cancellationToken : cancellationToken).ConfigureAwait(false); return(result.GetDocumentDiagnostics(documentId, kind)); } Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); var nonLocalResult = await state.GetProjectAnalysisDataAsync(Owner.PersistentStorageService, project, avoidLoadingData : false, cancellationToken : cancellationToken).ConfigureAwait(false); return(nonLocalResult.GetOtherDiagnostics()); }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, ImmutableArray<DiagnosticData> oldItems, ImmutableArray<DiagnosticData> newItems, Action<DiagnosticsUpdatedArgs> raiseEvents) { if (oldItems.IsEmpty && newItems.IsEmpty) { // there is nothing to update return; } RaiseDiagnosticsCreated(document, stateSet, kind, newItems, raiseEvents); }
private async Task<IEnumerable<Diagnostic>> ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync( Document document, DocumentDiagnosticAnalyzer analyzer, AnalysisKind kind, Compilation compilationOpt, CancellationToken cancellationToken) { using (var pooledObject = SharedPools.Default<List<Diagnostic>>().GetPooledObject()) { var diagnostics = pooledObject.Object; cancellationToken.ThrowIfCancellationRequested(); try { switch (kind) { case AnalysisKind.Syntax: await analyzer.AnalyzeSyntaxAsync(document, diagnostics.Add, cancellationToken).ConfigureAwait(false); return compilationOpt == null ? diagnostics.ToImmutableArrayOrEmpty() : CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt); case AnalysisKind.Semantic: await analyzer.AnalyzeSemanticsAsync(document, diagnostics.Add, cancellationToken).ConfigureAwait(false); return compilationOpt == null ? diagnostics.ToImmutableArrayOrEmpty() : CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt); default: return Contract.FailWithReturn<ImmutableArray<Diagnostic>>("shouldn't reach here"); } } catch (Exception e) when (!IsCanceled(e, cancellationToken)) { OnAnalyzerException(analyzer, document.Project.Id, compilationOpt, e); return ImmutableArray<Diagnostic>.Empty; } } }