-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Moved ISolutionCrawlerService down to workspace layer and made misc to use common default diagnostic analyzer service #11158
Changes from 3 commits
7ae5d35
83c28a6
b3d9c72
dc8338f
d0656e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,24 +9,26 @@ | |
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Options; | ||
using Microsoft.CodeAnalysis.Shared.Extensions; | ||
using Microsoft.CodeAnalysis.Shared.Options; | ||
using Microsoft.CodeAnalysis.SolutionCrawler; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics | ||
{ | ||
[ExportIncrementalAnalyzerProvider(WorkspaceKind.MiscellaneousFiles)] | ||
[Shared] | ||
internal partial class MiscellaneousDiagnosticAnalyzerService : IIncrementalAnalyzerProvider, IDiagnosticUpdateSource | ||
[ExportIncrementalAnalyzerProvider(WellKnownSolutionCrawlerAnalyzers.Diagnostic, workspaceKinds: null)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. null indicates default |
||
internal partial class DefaultDiagnosticAnalyzerService : IIncrementalAnalyzerProvider, IDiagnosticUpdateSource | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. defalut diagnostic analyzer service |
||
private const int Syntax = 1; | ||
private const int Semantic = 2; | ||
|
||
private readonly IDiagnosticAnalyzerService _analyzerService; | ||
|
||
[ImportingConstructor] | ||
public MiscellaneousDiagnosticAnalyzerService(IDiagnosticAnalyzerService analyzerService, IDiagnosticUpdateSourceRegistrationService registrationService) | ||
public DefaultDiagnosticAnalyzerService( | ||
IDiagnosticAnalyzerService analyzerService, IDiagnosticUpdateSourceRegistrationService registrationService) | ||
{ | ||
_analyzerService = analyzerService; | ||
|
||
registrationService.Register(this); | ||
} | ||
|
||
|
@@ -37,7 +39,7 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) | |
return null; | ||
} | ||
|
||
return new SyntaxOnlyDiagnosticAnalyzer(this, workspace); | ||
return new CompilerDiagnosticAnalyzer(this, workspace); | ||
} | ||
|
||
public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated; | ||
|
@@ -62,21 +64,33 @@ internal void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs state) | |
this.DiagnosticsUpdated?.Invoke(this, state); | ||
} | ||
|
||
private class SyntaxOnlyDiagnosticAnalyzer : IIncrementalAnalyzer | ||
private class CompilerDiagnosticAnalyzer : IIncrementalAnalyzer | ||
{ | ||
private readonly MiscellaneousDiagnosticAnalyzerService _service; | ||
private readonly DefaultDiagnosticAnalyzerService _service; | ||
private readonly Workspace _workspace; | ||
|
||
public SyntaxOnlyDiagnosticAnalyzer(MiscellaneousDiagnosticAnalyzerService service, Workspace workspace) | ||
public CompilerDiagnosticAnalyzer(DefaultDiagnosticAnalyzerService service, Workspace workspace) | ||
{ | ||
_service = service; | ||
_workspace = workspace; | ||
} | ||
|
||
public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) | ||
{ | ||
if (e.Option == InternalRuntimeDiagnosticOptions.Syntax || | ||
e.Option == InternalRuntimeDiagnosticOptions.Semantic) | ||
{ | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) | ||
{ | ||
// if closed file diagnostic is off and document is not opened, then don't do anything | ||
if (!CheckOptions(document)) | ||
// right now, there is no way to observe diagnostics for closed file. | ||
if (!_workspace.IsDocumentOpen(document.Id) || | ||
!_workspace.Options.GetOption(InternalRuntimeDiagnosticOptions.Syntax)) | ||
{ | ||
return; | ||
} | ||
|
@@ -87,25 +101,45 @@ public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancel | |
Contract.Requires(document.Project.Solution.Workspace == _workspace); | ||
|
||
var diagnosticData = diagnostics == null ? ImmutableArray<DiagnosticData>.Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty(); | ||
|
||
_service.RaiseDiagnosticsUpdated( | ||
DiagnosticsUpdatedArgs.DiagnosticsCreated(new DefaultUpdateArgsId(_workspace.Kind, Syntax, document.Id), | ||
_workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); | ||
} | ||
|
||
public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) | ||
{ | ||
// right now, there is no way to observe diagnostics for closed file. | ||
if (!_workspace.IsDocumentOpen(document.Id) || | ||
!_workspace.Options.GetOption(InternalRuntimeDiagnosticOptions.Semantic)) | ||
{ | ||
return; | ||
} | ||
|
||
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); | ||
var diagnostics = model.GetMethodBodyDiagnostics(span: null, cancellationToken: cancellationToken).Concat( | ||
model.GetDeclarationDiagnostics(span: null, cancellationToken: cancellationToken)); | ||
|
||
Contract.Requires(document.Project.Solution.Workspace == _workspace); | ||
|
||
var diagnosticData = diagnostics == null ? ImmutableArray<DiagnosticData>.Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty(); | ||
|
||
_service.RaiseDiagnosticsUpdated( | ||
DiagnosticsUpdatedArgs.DiagnosticsCreated(new MiscUpdateArgsId(document.Id), | ||
DiagnosticsUpdatedArgs.DiagnosticsCreated(new DefaultUpdateArgsId(_workspace.Kind, Semantic, document.Id), | ||
_workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); | ||
} | ||
|
||
public void RemoveDocument(DocumentId documentId) | ||
{ | ||
// a file is removed from misc project | ||
RaiseEmptyDiagnosticUpdated(documentId); | ||
RaiseEmptyDiagnosticUpdated(Syntax, documentId); | ||
RaiseEmptyDiagnosticUpdated(Semantic, documentId); | ||
} | ||
|
||
public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) | ||
{ | ||
// no closed file diagnostic and file is not opened, remove any existing diagnostics | ||
if (!CheckOptions(document)) | ||
{ | ||
RaiseEmptyDiagnosticUpdated(document.Id); | ||
} | ||
|
||
RemoveDocument(document.Id); | ||
return SpecializedTasks.EmptyTask; | ||
} | ||
|
||
|
@@ -114,16 +148,10 @@ public Task DocumentCloseAsync(Document document, CancellationToken cancellation | |
return DocumentResetAsync(document, cancellationToken); | ||
} | ||
|
||
private void RaiseEmptyDiagnosticUpdated(DocumentId documentId) | ||
private void RaiseEmptyDiagnosticUpdated(int kind, DocumentId documentId) | ||
{ | ||
_service.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs.DiagnosticsRemoved( | ||
ValueTuple.Create(this, documentId), _workspace, null, documentId.ProjectId, documentId)); | ||
} | ||
|
||
// method we don't care. misc project only supports syntax errors | ||
public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) | ||
{ | ||
return SpecializedTasks.EmptyTask; | ||
new DefaultUpdateArgsId(_workspace.Kind, kind, documentId), _workspace, null, documentId.ProjectId, documentId)); | ||
} | ||
|
||
public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) | ||
|
@@ -136,11 +164,6 @@ public Task DocumentOpenAsync(Document document, CancellationToken cancellationT | |
return SpecializedTasks.EmptyTask; | ||
} | ||
|
||
public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) | ||
{ | ||
return false; | ||
} | ||
|
||
public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) | ||
{ | ||
return SpecializedTasks.EmptyTask; | ||
|
@@ -150,45 +173,31 @@ public void RemoveProject(ProjectId projectId) | |
{ | ||
} | ||
|
||
private bool CheckOptions(Document document) | ||
private class DefaultUpdateArgsId : BuildToolId.Base<int, DocumentId>, ISupportLiveUpdate | ||
{ | ||
if (ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(_workspace, document.Project.Language) && | ||
_workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysis)) | ||
{ | ||
return true; | ||
} | ||
|
||
return document.IsOpen(); | ||
} | ||
private readonly string _workspaceKind; | ||
|
||
private class MiscUpdateArgsId : BuildToolId.Base<DocumentId>, ISupportLiveUpdate | ||
{ | ||
public MiscUpdateArgsId(DocumentId documentId) : base(documentId) | ||
public DefaultUpdateArgsId(string workspaceKind, int type, DocumentId documentId) : base(type, documentId) | ||
{ | ||
_workspaceKind = workspaceKind; | ||
} | ||
|
||
public override string BuildTool | ||
{ | ||
get | ||
{ | ||
return PredefinedBuildTools.Live; | ||
} | ||
} | ||
public override string BuildTool => PredefinedBuildTools.Live; | ||
|
||
public override bool Equals(object obj) | ||
{ | ||
var other = obj as MiscUpdateArgsId; | ||
var other = obj as DefaultUpdateArgsId; | ||
if (other == null) | ||
{ | ||
return false; | ||
} | ||
|
||
return base.Equals(obj); | ||
return _workspaceKind == other._workspaceKind && base.Equals(obj); | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
return base.GetHashCode(); | ||
return Hash.Combine(_workspaceKind.GetHashCode(), base.GetHashCode()); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
using System; | ||
using System.Collections.Concurrent; | ||
using System.Composition; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis.Options; | ||
|
@@ -13,7 +14,7 @@ | |
namespace Microsoft.CodeAnalysis.Notification | ||
{ | ||
[Export(typeof(ISemanticChangeNotificationService)), Shared] | ||
[ExportIncrementalAnalyzerProvider(WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.MiscellaneousFiles)] | ||
[ExportIncrementalAnalyzerProvider(nameof(SemanticChangeNotificationService), workspaceKinds: null)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. made this analyzer to run for all workspaces. |
||
internal class SemanticChangeNotificationService : ISemanticChangeNotificationService, IIncrementalAnalyzerProvider | ||
{ | ||
public event EventHandler<Document> OpenedDocumentSemanticChanged; | ||
|
@@ -38,17 +39,36 @@ public NotificationService(SemanticChangeNotificationService owner) | |
_owner = owner; | ||
} | ||
|
||
public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) | ||
public void RemoveDocument(DocumentId documentId) | ||
{ | ||
// now it runs for all workspace, make sure we get rid of entry from the map | ||
// as soon as it is not needed. | ||
// this whole thing will go away when workspace disable itself from solution crawler. | ||
VersionStamp unused; | ||
_map.TryRemove(document.Id, out unused); | ||
_map.TryRemove(documentId, out unused); | ||
} | ||
|
||
public void RemoveProject(ProjectId projectId) | ||
{ | ||
foreach (var documentId in _map.Keys.Where(id => id.ProjectId == projectId).ToArray()) | ||
{ | ||
RemoveDocument(documentId); | ||
} | ||
} | ||
|
||
public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) | ||
{ | ||
return DocumentResetAsync(document, cancellationToken); | ||
} | ||
|
||
public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) | ||
{ | ||
RemoveDocument(document.Id); | ||
return SpecializedTasks.EmptyTask; | ||
} | ||
|
||
public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) | ||
{ | ||
// TODO: Is this correct? | ||
return false; | ||
} | ||
|
||
|
@@ -81,11 +101,6 @@ public Task DocumentOpenAsync(Document document, CancellationToken cancellationT | |
return SpecializedTasks.EmptyTask; | ||
} | ||
|
||
public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) | ||
{ | ||
return SpecializedTasks.EmptyTask; | ||
} | ||
|
||
public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) | ||
{ | ||
return SpecializedTasks.EmptyTask; | ||
|
@@ -101,13 +116,6 @@ public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, Cancella | |
return SpecializedTasks.EmptyTask; | ||
} | ||
|
||
public void RemoveDocument(DocumentId documentId) | ||
{ | ||
} | ||
|
||
public void RemoveProject(ProjectId projectId) | ||
{ | ||
} | ||
#endregion | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ISolutionCrawlerRegisterationService moved down to workspace layer