From 393b21821d6035736530c62dbcff9a0f29d9f4a2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Sep 2022 10:28:41 -0700 Subject: [PATCH 1/8] Move diagnostic tagging to 'pull diagnostics' --- .../InlineDiagnosticsTaggerProvider.cs | 3 +- ...tractDiagnosticsAdornmentTaggerProvider.cs | 20 ++- .../AbstractDiagnosticsTaggerProvider.cs | 147 ++---------------- ...DiagnosticsClassificationTaggerProvider.cs | 5 +- .../DiagnosticsSquiggleTaggerProvider.cs | 3 +- .../DiagnosticsSuggestionTaggerProvider.cs | 5 +- .../DocumentDiagnosticSource.cs | 5 +- 7 files changed, 38 insertions(+), 150 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs index 9d8b8ee141a5b..1b1cdffc538dd 100644 --- a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs @@ -41,13 +41,14 @@ internal class InlineDiagnosticsTaggerProvider : AbstractDiagnosticsAdornmentTag public InlineDiagnosticsTaggerProvider( IThreadingContext threadingContext, IDiagnosticService diagnosticService, + IDiagnosticAnalyzerService analyzerService, IGlobalOptionService globalOptions, [Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker, IAsynchronousOperationListenerProvider listenerProvider, IEditorFormatMapService editorFormatMapService, IClassificationFormatMapService classificationFormatMapService, IClassificationTypeRegistryService classificationTypeRegistryService) - : base(threadingContext, diagnosticService, globalOptions, visibilityTracker, listenerProvider) + : base(threadingContext, diagnosticService, analyzerService, globalOptions, visibilityTracker, listenerProvider) { _editorFormatMap = editorFormatMapService.GetEditorFormatMap("text"); _classificationFormatMapService = classificationFormatMapService; diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs index 88c97ee7ff139..729ee70ccb197 100644 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs @@ -22,31 +22,28 @@ internal abstract class AbstractDiagnosticsAdornmentTaggerProvider : protected AbstractDiagnosticsAdornmentTaggerProvider( IThreadingContext threadingContext, IDiagnosticService diagnosticService, + IDiagnosticAnalyzerService analyzerService, IGlobalOptionService globalOptions, ITextBufferVisibilityTracker? visibilityTracker, IAsynchronousOperationListenerProvider listenerProvider) - : base(threadingContext, diagnosticService, globalOptions, visibilityTracker, listenerProvider.GetListener(FeatureAttribute.ErrorSquiggles)) + : base(threadingContext, diagnosticService, analyzerService, globalOptions, visibilityTracker, listenerProvider.GetListener(FeatureAttribute.ErrorSquiggles)) { } protected internal sealed override bool IsEnabled => true; protected internal sealed override ITagSpan? CreateTagSpan( - Workspace workspace, bool isLiveUpdate, SnapshotSpan span, DiagnosticData data) + Workspace workspace, SnapshotSpan span, DiagnosticData data) { var errorTag = CreateTag(workspace, data); if (errorTag == null) - { return null; - } - // Live update squiggles have to be at least 1 character long. - var minimumLength = isLiveUpdate ? 1 : 0; - var adjustedSpan = AdjustSnapshotSpan(span, minimumLength); + // Ensure the diagnostic has at least length 1. Tags must have a non-empty length in order to actually show + // up in the editor. + var adjustedSpan = AdjustSnapshotSpan(span); if (adjustedSpan.Length == 0) - { return null; - } return new TagSpan(adjustedSpan, errorTag); } @@ -78,8 +75,9 @@ protected static object CreateToolTipContent(Workspace workspace, DiagnosticData new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Message))); } - protected virtual SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength) - => AdjustSnapshotSpan(span, minimumLength, int.MaxValue); + // By default, tags must have at least length '1' so that they can be visible in the UI layer. + protected virtual SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span) + => AdjustSnapshotSpan(span, minimumLength: 1, maximumLength: int.MaxValue); protected static SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength, int maximumLength) { diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs index 8b5b9eddac72b..1c256fc86a3e5 100644 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Immutable; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; @@ -14,9 +13,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; @@ -24,95 +21,36 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { /// - /// Diagnostics works slightly differently than the rest of the taggers. For diagnostics, - /// we want to try to have an individual tagger per diagnostic producer per buffer. - /// However, the editor only allows a single tagger provider per buffer. So in order to - /// get the abstraction we want, we create one outer tagger provider that is associated - /// with the buffer. Then, under the covers, we create individual async taggers for each - /// diagnostic producer we hear about for that buffer. - /// - /// In essence, we have one tagger that wraps a multitude of taggers it delegates to. - /// Each of these taggers is nicely asynchronous and properly works within the async - /// tagging infrastructure. + /// Base type for all taggers that interact with the and produce tags for + /// the diagnostics with different UI presentations. /// internal abstract partial class AbstractDiagnosticsTaggerProvider : AsynchronousTaggerProvider where TTag : ITag { private readonly IDiagnosticService _diagnosticService; - - /// - /// Keep track of the ITextSnapshot for the open Document that was used when diagnostics were - /// produced for it. We need that because the DiagnoticService does not keep track of this - /// snapshot (so as to not hold onto a lot of memory), which means when we query it for - /// diagnostics, we don't know how to map the span of the diagnostic to the current snapshot - /// we're tagging. - /// - private static readonly ConditionalWeakTable _diagnosticIdToTextSnapshot = new(); + private readonly IDiagnosticAnalyzerService _analyzerService; protected AbstractDiagnosticsTaggerProvider( IThreadingContext threadingContext, IDiagnosticService diagnosticService, + IDiagnosticAnalyzerService analyzerService, IGlobalOptionService globalOptions, ITextBufferVisibilityTracker? visibilityTracker, IAsynchronousOperationListener listener) : base(threadingContext, globalOptions, visibilityTracker, listener) { _diagnosticService = diagnosticService; - _diagnosticService.DiagnosticsUpdated += OnDiagnosticsUpdated; + _analyzerService = analyzerService; } protected internal abstract bool IsEnabled { get; } protected internal abstract bool SupportsDiagnosticMode(DiagnosticMode mode); protected internal abstract bool IncludeDiagnostic(DiagnosticData data); - protected internal abstract ITagSpan? CreateTagSpan(Workspace workspace, bool isLiveUpdate, SnapshotSpan span, DiagnosticData data); - - private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e) - { - if (e.Solution == null || e.DocumentId == null) - { - return; - } - - if (_diagnosticIdToTextSnapshot.TryGetValue(e.Id, out var snapshot)) - { - return; - } - - var document = e.Solution.GetDocument(e.DocumentId); - - // If we couldn't find a normal document, and all features are enabled for source generated documents, - // attempt to locate a matching source generated document in the project. - if (document is null - && e.Workspace.Services.GetService()?.Options.EnableOpeningSourceGeneratedFiles == true - && e.Solution.GetProject(e.DocumentId.ProjectId) is { } project) - { - var documentId = e.DocumentId; - document = ThreadingContext.JoinableTaskFactory.Run(() => project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).AsTask()); - } - - // Open documents *should* always have their SourceText available, but we cannot guarantee - // (i.e. assert) that they do. That's because we're not on the UI thread here, so there's - // a small risk that between calling .IsOpen the file may then close, which then would - // cause TryGetText to fail. However, that's ok. In that case, if we do need to tag this - // document, we'll just use the current editor snapshot. If that's the same, then the tags - // will be hte same. If it is different, we'll eventually hear about the new diagnostics - // for it and we'll reach our fixed point. - if (document != null && document.IsOpen()) - { - // This should always be fast since the document is open. - var sourceText = document.State.GetTextSynchronously(cancellationToken: default); - snapshot = sourceText.FindCorrespondingEditorTextSnapshot(); - if (snapshot != null) - { - _diagnosticIdToTextSnapshot.GetValue(e.Id, _ => snapshot); - } - } - } + protected internal abstract ITagSpan? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data); protected override TaggerDelay EventChangeDelay => TaggerDelay.Short; protected override TaggerDelay AddedTagNotificationDelay => TaggerDelay.OnIdle; @@ -137,7 +75,7 @@ protected override ITaggerEventSource CreateEventSource(ITextView? textView, ITe /// the diagnostic containing the location(s). /// an array of locations that should have the tag applied. protected internal virtual ImmutableArray GetLocationsToTag(DiagnosticData diagnosticData) - => diagnosticData.DataLocation is object ? ImmutableArray.Create(diagnosticData.DataLocation) : ImmutableArray.Empty; + => diagnosticData.DataLocation is not null ? ImmutableArray.Create(diagnosticData.DataLocation) : ImmutableArray.Empty; protected override Task ProduceTagsAsync( TaggerContext context, DocumentSnapshotSpan spanToTag, int? caretPosition, CancellationToken cancellationToken) @@ -170,60 +108,14 @@ private async Task ProduceTagsAsync( var suppressedDiagnosticsSpans = (NormalizedSnapshotSpanCollection?)null; buffer?.Properties.TryGetProperty(PredefinedPreviewTaggerKeys.SuppressDiagnosticsSpansKey, out suppressedDiagnosticsSpans); - var buckets = diagnosticMode switch - { - DiagnosticMode.Pull => _diagnosticService.GetPullDiagnosticBuckets(workspace, document.Project.Id, document.Id, diagnosticMode, cancellationToken), - DiagnosticMode.Push => _diagnosticService.GetPushDiagnosticBuckets(workspace, document.Project.Id, document.Id, diagnosticMode, cancellationToken), - _ => throw ExceptionUtilities.UnexpectedValue(diagnosticMode), - }; - - foreach (var bucket in buckets) - { - await ProduceTagsAsync( - context, spanToTag, workspace, document, - suppressedDiagnosticsSpans, bucket, cancellationToken).ConfigureAwait(false); - } - } + var sourceText = editorSnapshot.AsText(); - private async Task ProduceTagsAsync( - TaggerContext context, DocumentSnapshotSpan spanToTag, - Workspace workspace, Document document, - NormalizedSnapshotSpanCollection? suppressedDiagnosticsSpans, - DiagnosticBucket bucket, CancellationToken cancellationToken) - { try { - var diagnosticMode = GlobalOptions.GetDiagnosticMode(InternalDiagnosticsOptions.NormalDiagnosticMode); - - var id = bucket.Id; - var diagnostics = await _diagnosticService.GetPushDiagnosticsAsync( - workspace, document.Project.Id, document.Id, id, - includeSuppressedDiagnostics: false, - diagnosticMode, - cancellationToken).ConfigureAwait(false); - - var isLiveUpdate = id is ISupportLiveUpdate; + var diagnostics = await _analyzerService.GetDiagnosticsForSpanAsync( + document, range: null, cancellationToken: cancellationToken).ConfigureAwait(false); var requestedSpan = spanToTag.SnapshotSpan; - var editorSnapshot = requestedSpan.Snapshot; - - // Try to get the text snapshot that these diagnostics were created against. - // This may fail if this tagger was created *after* the notification for the - // diagnostics was already issued. That's ok. We'll take the spans as reported - // and apply them directly to the snapshot we have. Either no new changes will - // have happened, and these spans will be accurate, or a change will happen - // and we'll hear about and it update the spans shortly to the right position. - // - // Also, only use the diagnoticSnapshot if its text buffer matches our. The text - // buffer might be different if the file was closed/reopened. - // Note: when this happens, the diagnostic service will reanalyze the file. So - // up to date diagnostic spans will appear shortly after this. - _diagnosticIdToTextSnapshot.TryGetValue(id, out var diagnosticSnapshot); - diagnosticSnapshot = diagnosticSnapshot?.TextBuffer == editorSnapshot.TextBuffer - ? diagnosticSnapshot - : editorSnapshot; - - var sourceText = diagnosticSnapshot.AsText(); foreach (var diagnosticData in diagnostics) { @@ -241,16 +133,14 @@ private async Task ProduceTagsAsync( // editorSnapshot. var diagnosticSpans = this.GetLocationsToTag(diagnosticData) - .Select(location => GetDiagnosticSnapshotSpan(location, diagnosticSnapshot, editorSnapshot, sourceText)); + .Select(location => GetDiagnosticSnapshotSpan(location, editorSnapshot, sourceText)); foreach (var diagnosticSpan in diagnosticSpans) { if (diagnosticSpan.IntersectsWith(requestedSpan) && !IsSuppressed(suppressedDiagnosticsSpans, diagnosticSpan)) { - var tagSpan = this.CreateTagSpan(workspace, isLiveUpdate, diagnosticSpan, diagnosticData); + var tagSpan = this.CreateTagSpan(workspace, diagnosticSpan, diagnosticData); if (tagSpan != null) - { context.AddTag(tagSpan); - } } } } @@ -259,18 +149,13 @@ private async Task ProduceTagsAsync( catch (ArgumentOutOfRangeException ex) when (FatalError.ReportAndCatch(ex)) { // https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=428328&_a=edit&triage=false - // explicitly report NFW to find out what is causing us for out of range. - // stop crashing on such occasions + // explicitly report NFW to find out what is causing us for out of range. stop crashing on such + // occasions return; } - static SnapshotSpan GetDiagnosticSnapshotSpan(DiagnosticDataLocation diagnosticDataLocation, ITextSnapshot diagnosticSnapshot, - ITextSnapshot editorSnapshot, SourceText sourceText) - { - return DiagnosticData.GetExistingOrCalculatedTextSpan(diagnosticDataLocation, sourceText) - .ToSnapshotSpan(diagnosticSnapshot) - .TranslateTo(editorSnapshot, SpanTrackingMode.EdgeExclusive); - } + static SnapshotSpan GetDiagnosticSnapshotSpan(DiagnosticDataLocation diagnosticDataLocation, ITextSnapshot editorSnapshot, SourceText sourceText) + => DiagnosticData.GetExistingOrCalculatedTextSpan(diagnosticDataLocation, sourceText).ToSnapshotSpan(editorSnapshot); } private static bool IsSuppressed(NormalizedSnapshotSpanCollection? suppressedSpans, SnapshotSpan span) diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs index 9f364779b50c5..32124dda9cb28 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs @@ -46,11 +46,12 @@ internal partial class DiagnosticsClassificationTaggerProvider : AbstractDiagnos public DiagnosticsClassificationTaggerProvider( IThreadingContext threadingContext, IDiagnosticService diagnosticService, + IDiagnosticAnalyzerService analyzerService, ClassificationTypeMap typeMap, EditorOptionsService editorOptionsService, [Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker, IAsynchronousOperationListenerProvider listenerProvider) - : base(threadingContext, diagnosticService, editorOptionsService.GlobalOptions, visibilityTracker, listenerProvider.GetListener(FeatureAttribute.Classification)) + : base(threadingContext, diagnosticService, analyzerService, editorOptionsService.GlobalOptions, visibilityTracker, listenerProvider.GetListener(FeatureAttribute.Classification)) { _typeMap = typeMap; _classificationTag = new ClassificationTag(_typeMap.GetClassificationType(ClassificationTypeDefinitions.UnnecessaryCode)); @@ -92,7 +93,7 @@ protected internal override bool IncludeDiagnostic(DiagnosticData data) return true; } - protected internal override ITagSpan CreateTagSpan(Workspace workspace, bool isLiveUpdate, SnapshotSpan span, DiagnosticData data) + protected internal override ITagSpan CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data) => new TagSpan(span, _classificationTag); protected internal override ImmutableArray GetLocationsToTag(DiagnosticData diagnosticData) diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs index 47ec2137d3d02..370f685f7bf13 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs @@ -37,10 +37,11 @@ internal partial class DiagnosticsSquiggleTaggerProvider : AbstractDiagnosticsAd public DiagnosticsSquiggleTaggerProvider( IThreadingContext threadingContext, IDiagnosticService diagnosticService, + IDiagnosticAnalyzerService analyzerService, IGlobalOptionService globalOptions, [Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker, IAsynchronousOperationListenerProvider listenerProvider) - : base(threadingContext, diagnosticService, globalOptions, visibilityTracker, listenerProvider) + : base(threadingContext, diagnosticService, analyzerService, globalOptions, visibilityTracker, listenerProvider) { } diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs index 0964a05bdcf26..941237ad55813 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs @@ -37,10 +37,11 @@ internal sealed partial class DiagnosticsSuggestionTaggerProvider : public DiagnosticsSuggestionTaggerProvider( IThreadingContext threadingContext, IDiagnosticService diagnosticService, + IDiagnosticAnalyzerService analyzerService, IGlobalOptionService globalOptions, [Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker, IAsynchronousOperationListenerProvider listenerProvider) - : base(threadingContext, diagnosticService, globalOptions, visibilityTracker, listenerProvider) + : base(threadingContext, diagnosticService, analyzerService, globalOptions, visibilityTracker, listenerProvider) { } @@ -59,7 +60,7 @@ protected override IErrorTag CreateTag(Workspace workspace, DiagnosticData diagn PredefinedErrorTypeNames.HintedSuggestion, CreateToolTipContent(workspace, diagnostic)); - protected override SnapshotSpan AdjustSnapshotSpan(SnapshotSpan snapshotSpan, int minimumLength) + protected override SnapshotSpan AdjustSnapshotSpan(SnapshotSpan snapshotSpan) { // We always want suggestion tags to be two characters long. return AdjustSnapshotSpan(snapshotSpan, minimumLength: 2, maximumLength: 2); diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs index 145ac97b3d70e..cb86ca3f14c2e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs @@ -18,8 +18,9 @@ internal sealed record class DocumentDiagnosticSource(Document Document) : IDiag public async Task> GetDiagnosticsAsync(IDiagnosticAnalyzerService diagnosticAnalyzerService, RequestContext context, DiagnosticMode diagnosticMode, CancellationToken cancellationToken) { - // We call GetDiagnosticsForSpanAsync here instead of GetDiagnosticsForIdsAsync as it has faster perf characteristics. - // GetDiagnosticsForIdsAsync runs analyzers against the entire compilation whereas GetDiagnosticsForSpanAsync will only run analyzers against the request document. + // We call GetDiagnosticsForSpanAsync here instead of GetDiagnosticsForIdsAsync as it has faster perf + // characteristics. GetDiagnosticsForIdsAsync runs analyzers against the entire compilation whereas + // GetDiagnosticsForSpanAsync will only run analyzers against the request document. var allSpanDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForSpanAsync(Document, range: null, cancellationToken: cancellationToken).ConfigureAwait(false); return allSpanDiagnostics; } From 8358e7c02f70e2a4f991819efd27fd81943058be Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Sep 2022 10:38:35 -0700 Subject: [PATCH 2/8] Remove pull diagnostic helper on IDiagnosticService --- .../Test/Diagnostics/MockDiagnosticService.cs | 5 ----- .../Features/Diagnostics/DiagnosticService.cs | 3 --- .../Features/Diagnostics/IDiagnosticService.cs | 11 ----------- .../Diagnostics/DiagnosticTableDataSourceTests.vb | 4 ---- 4 files changed, 23 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs index 3daad491a914c..d2b1c55ff5c92 100644 --- a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs +++ b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs @@ -46,11 +46,6 @@ private ImmutableArray GetDiagnostics(Workspace workspace, Proje return _diagnostic == null ? ImmutableArray.Empty : ImmutableArray.Create(_diagnostic); } - public ImmutableArray GetPullDiagnosticBuckets(Workspace workspace, ProjectId? projectId, DocumentId? documentId, DiagnosticMode diagnosticMode, CancellationToken cancellationToken) - { - return GetDiagnosticBuckets(workspace, projectId, documentId); - } - public ImmutableArray GetPushDiagnosticBuckets(Workspace workspace, ProjectId? projectId, DocumentId? documentId, DiagnosticMode diagnosticMode, CancellationToken cancellationToken) { return GetDiagnosticBuckets(workspace, projectId, documentId); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService.cs index 19cb071f9d85e..53d4c87a5fa9c 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService.cs @@ -303,9 +303,6 @@ private async ValueTask> GetDiagnosticsAsync( return result.ToImmutable(); } - public ImmutableArray GetPullDiagnosticBuckets(Workspace workspace, ProjectId projectId, DocumentId documentId, DiagnosticMode diagnosticMode, CancellationToken cancellationToken) - => GetDiagnosticBuckets(workspace, projectId, documentId, forPullDiagnostics: true, diagnosticMode, cancellationToken); - public ImmutableArray GetPushDiagnosticBuckets(Workspace workspace, ProjectId projectId, DocumentId documentId, DiagnosticMode diagnosticMode, CancellationToken cancellationToken) => GetDiagnosticBuckets(workspace, projectId, documentId, forPullDiagnostics: false, diagnosticMode, cancellationToken); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/IDiagnosticService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/IDiagnosticService.cs index 8169798ba0c72..862f2bfa97217 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/IDiagnosticService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/IDiagnosticService.cs @@ -34,17 +34,6 @@ ValueTask> GetPushDiagnosticsAsync( Workspace workspace, ProjectId? projectId, DocumentId? documentId, object? id, bool includeSuppressedDiagnostics, DiagnosticMode diagnosticMode, CancellationToken cancellationToken); - /// - /// Get current buckets storing our grouped diagnostics. - /// - /// Option controlling if pull diagnostics are allowed for the client. The - /// only provides diagnostics for either push or pull purposes (but not both). - /// If the caller's desired purpose doesn't match the option value, then this will return nothing, otherwise it - /// will return the requested buckets. - ImmutableArray GetPullDiagnosticBuckets( - Workspace workspace, ProjectId? projectId, DocumentId? documentId, - DiagnosticMode diagnosticMode, CancellationToken cancellationToken); - /// /// Get current buckets storing our grouped diagnostics. /// diff --git a/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb index 9a7ac01524e4e..8acfbe2ff325e 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb @@ -837,10 +837,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return diagnostics End Function - Public Function GetPullDiagnosticBuckets(workspace As Workspace, projectId As ProjectId, documentId As DocumentId, diagnosticMode As DiagnosticMode, cancellationToken As CancellationToken) As ImmutableArray(Of DiagnosticBucket) Implements IDiagnosticService.GetPullDiagnosticBuckets - Return GetDiagnosticsBuckets(workspace, projectId, documentId) - End Function - Public Function GetPushDiagnosticBuckets(workspace As Workspace, projectId As ProjectId, documentId As DocumentId, diagnosticMode As DiagnosticMode, cancellationToken As CancellationToken) As ImmutableArray(Of DiagnosticBucket) Implements IDiagnosticService.GetPushDiagnosticBuckets Return GetDiagnosticsBuckets(workspace, projectId, documentId) End Function From 3a02eef179b46369150d15711bd39389a21890a7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Sep 2022 13:19:22 -0700 Subject: [PATCH 3/8] Update tests --- .../DiagnosticsSquiggleTaggerProviderTests.cs | 11 ++++++++--- .../Test/Diagnostics/MockDiagnosticService.cs | 18 +++++++++--------- .../Helpers/MockDiagnosticAnalyzerService.cs | 8 +++++++- .../RemoteEditAndContinueServiceTests.cs | 4 ++-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs index 8be763b7bf450..20415987ac5ee 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -29,7 +30,9 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics public class DiagnosticsSquiggleTaggerProviderTests { private static readonly TestComposition s_compositionWithMockDiagnosticService = - EditorTestCompositions.EditorFeatures.AddExcludedPartTypes(typeof(IDiagnosticService)).AddParts(typeof(MockDiagnosticService)); + EditorTestCompositions.EditorFeatures + .AddExcludedPartTypes(typeof(IDiagnosticService), typeof(IDiagnosticAnalyzerService)) + .AddParts(typeof(MockDiagnosticService), typeof(MockDiagnosticAnalyzerService)); [WpfFact, Trait(Traits.Feature, Traits.Features.Diagnostics)] public async Task Test_TagSourceDiffer() @@ -124,6 +127,7 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedBeforeIniti var listenerProvider = workspace.ExportProvider.GetExportedValue(); var diagnosticService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); + var analyzerService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); var provider = workspace.ExportProvider.GetExportedValues().OfType().Single(); // Create the tagger before the first diagnostic event has been fired. @@ -133,7 +137,7 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedBeforeIniti // Now product the first diagnostic and fire the events. var tree = await workspace.CurrentSolution.Projects.Single().Documents.Single().GetRequiredSyntaxTreeAsync(CancellationToken.None); var span = TextSpan.FromBounds(0, 5); - diagnosticService.CreateDiagnosticAndFireEvents(workspace, Location.Create(tree, span)); + diagnosticService.CreateDiagnosticAndFireEvents(workspace, analyzerService, Location.Create(tree, span)); using var disposable = tagger as IDisposable; await listenerProvider.GetWaiter(FeatureAttribute.DiagnosticService).ExpeditedWaitAsync(); @@ -162,12 +166,13 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedAfterInitia var listenerProvider = workspace.ExportProvider.GetExportedValue(); var diagnosticService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); + var analyzerService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); var provider = workspace.ExportProvider.GetExportedValues().OfType().Single(); // Create and fire the diagnostic events before the tagger is even made. var tree = await workspace.CurrentSolution.Projects.Single().Documents.Single().GetRequiredSyntaxTreeAsync(CancellationToken.None); var span = TextSpan.FromBounds(0, 5); - diagnosticService.CreateDiagnosticAndFireEvents(workspace, Location.Create(tree, span)); + diagnosticService.CreateDiagnosticAndFireEvents(workspace, analyzerService, Location.Create(tree, span)); var tagger = provider.CreateTagger(workspace.Documents.First().GetTextBuffer()); Contract.ThrowIfNull(tagger); diff --git a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs index d2b1c55ff5c92..8ba38e4d6a313 100644 --- a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs +++ b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; @@ -16,14 +17,12 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { - [Export(typeof(IDiagnosticService))] - [Shared] - [PartNotDiscoverable] + [Export(typeof(IDiagnosticService)), Shared, PartNotDiscoverable] internal class MockDiagnosticService : IDiagnosticService { public const string DiagnosticId = "MockId"; - private DiagnosticData? _diagnostic; + private DiagnosticData? _diagnosticData; public event EventHandler? DiagnosticsUpdated; @@ -43,7 +42,7 @@ private ImmutableArray GetDiagnostics(Workspace workspace, Proje Assert.Equal(projectId, GetProjectId(workspace)); Assert.Equal(documentId, GetDocumentId(workspace)); - return _diagnostic == null ? ImmutableArray.Empty : ImmutableArray.Create(_diagnostic); + return _diagnosticData == null ? ImmutableArray.Empty : ImmutableArray.Create(_diagnosticData); } public ImmutableArray GetPushDiagnosticBuckets(Workspace workspace, ProjectId? projectId, DocumentId? documentId, DiagnosticMode diagnosticMode, CancellationToken cancellationToken) @@ -56,22 +55,23 @@ private ImmutableArray GetDiagnosticBuckets(Workspace workspac Assert.Equal(projectId, GetProjectId(workspace)); Assert.Equal(documentId, GetDocumentId(workspace)); - return _diagnostic == null + return _diagnosticData == null ? ImmutableArray.Empty : ImmutableArray.Create(new DiagnosticBucket(this, workspace, GetProjectId(workspace), GetDocumentId(workspace))); } - internal void CreateDiagnosticAndFireEvents(Workspace workspace, Location location) + internal void CreateDiagnosticAndFireEvents(Workspace workspace, MockDiagnosticAnalyzerService analyzerService, Location location) { var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); - _diagnostic = DiagnosticData.Create(Diagnostic.Create(DiagnosticId, "MockCategory", "MockMessage", DiagnosticSeverity.Error, DiagnosticSeverity.Error, isEnabledByDefault: true, warningLevel: 0, + _diagnosticData = DiagnosticData.Create(Diagnostic.Create(DiagnosticId, "MockCategory", "MockMessage", DiagnosticSeverity.Error, DiagnosticSeverity.Error, isEnabledByDefault: true, warningLevel: 0, location: location), document); + analyzerService.DiagnosticData = _diagnosticData; DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( this, workspace, workspace.CurrentSolution, GetProjectId(workspace), GetDocumentId(workspace), - ImmutableArray.Create(_diagnostic))); + ImmutableArray.Create(_diagnosticData))); } private static DocumentId GetDocumentId(Workspace workspace) diff --git a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs index bf4affae92cd9..f0d6b4283813f 100644 --- a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs @@ -5,20 +5,26 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests { + [Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] internal class MockDiagnosticAnalyzerService : IDiagnosticAnalyzerService { public readonly List DocumentsToReanalyze = new(); + public DiagnosticData? DiagnosticData; + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public MockDiagnosticAnalyzerService(IGlobalOptionService globalOptions) { GlobalOptions = globalOptions; @@ -48,7 +54,7 @@ public Task> GetDiagnosticsForIdsAsync(Solution s => throw new NotImplementedException(); public Task> GetDiagnosticsForSpanAsync(Document document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, bool includeSuppressedDiagnostics = true, CodeActionRequestPriority priority = CodeActionRequestPriority.None, Func? addOperationScope = null, CancellationToken cancellationToken = default) - => throw new NotImplementedException(); + => DiagnosticData != null ? Task.FromResult(ImmutableArray.Create(DiagnosticData)) : throw new NotImplementedException(); public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) => throw new NotImplementedException(); diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 16afc6ed3ca84..2422636153080 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -46,7 +46,7 @@ public async Task Proxy(TestHost testHost) var localComposition = EditorTestCompositions.EditorFeatures.WithTestHostParts(testHost); if (testHost == TestHost.InProcess) { - localComposition = localComposition.AddParts(typeof(MockEditAndContinueWorkspaceService)); + localComposition = localComposition.AddParts(typeof(MockEditAndContinueWorkspaceService), typeof(MockDiagnosticAnalyzerService)); } using var localWorkspace = new TestWorkspace(composition: localComposition); @@ -80,7 +80,7 @@ public async Task Proxy(TestHost testHost) var project = solution.Projects.Single(); var document = project.Documents.Single(); - var mockDiagnosticService = new MockDiagnosticAnalyzerService(globalOptions); + var mockDiagnosticService = (MockDiagnosticAnalyzerService)localWorkspace.GetService(); void VerifyReanalyzeInvocation(ImmutableArray documentIds) { From 5b071b501b4833501a86f0ab767bc1f22451ea98 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Sep 2022 13:28:42 -0700 Subject: [PATCH 4/8] Update test --- .../CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs | 8 ++++---- .../Diagnostics/DefaultDiagnosticUpdateSourceTests.vb | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs index eeb74f466874a..c13209c8fee30 100644 --- a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs +++ b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs @@ -328,10 +328,10 @@ class Test var document = workspace.Documents.First(); var updateArgs = DiagnosticsUpdatedArgs.DiagnosticsCreated( - new LiveId(), workspace, workspace.CurrentSolution, document.Project.Id, document.Id, - ImmutableArray.Create( - TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 0)), - TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 1)))); + new LiveId(), workspace, workspace.CurrentSolution, document.Project.Id, document.Id, + ImmutableArray.Create( + TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 0)), + TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 1)))); var spans = await TestDiagnosticTagProducer.GetErrorsFromUpdateSource(workspace, updateArgs); diff --git a/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb index bf5f39e2ec6f5..79fa25920263c 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb @@ -44,8 +44,7 @@ class 123 { } Dim diagnosticService = DirectCast(workspace.ExportProvider.GetExportedValue(Of IDiagnosticService), DiagnosticService) - Dim miscService = GetDefaultDiagnosticAnalyzerService(workspace) - Assert.False(miscService.SupportGetDiagnostics) + Dim miscService = DirectCast(workspace.GetService(Of IDiagnosticAnalyzerService), DiagnosticAnalyzerService) DiagnosticProvider.Enable(workspace, DiagnosticProvider.Options.Syntax) From 46fdd612424ead9e309bb6b109295bb88db674d0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Sep 2022 13:40:31 -0700 Subject: [PATCH 5/8] Update test --- .../Squiggles/ErrorSquiggleProducerTests.cs | 23 +++++++++++-------- .../Test/Diagnostics/MockDiagnosticService.cs | 2 +- .../RemoteEditAndContinueServiceTests.cs | 4 +--- .../MockDiagnosticAnalyzerService.cs | 6 ++--- .../Squiggles/TestDiagnosticTagProducer.cs | 3 +++ 5 files changed, 22 insertions(+), 16 deletions(-) rename src/EditorFeatures/{Test/EditAndContinue/Helpers => TestUtilities/Diagnostics}/MockDiagnosticAnalyzerService.cs (94%) diff --git a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs index c13209c8fee30..ebe8af42dd5b4 100644 --- a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs +++ b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs @@ -15,9 +15,8 @@ using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Options; @@ -279,6 +278,10 @@ public async Task TestNoErrorsAfterProjectRemoved() Assert.True(spans.Count == 0); } + private static readonly TestComposition s_mockComposition = EditorTestCompositions.EditorFeatures + .AddExcludedPartTypes(typeof(IDiagnosticAnalyzerService)) + .AddParts(typeof(MockDiagnosticAnalyzerService)); + [WpfFact, Trait(Traits.Feature, Traits.Features.ErrorSquiggles)] public async Task BuildErrorZeroLengthSpan() { @@ -293,21 +296,23 @@ class Test "; - using var workspace = TestWorkspace.Create(workspaceXml); + using var workspace = TestWorkspace.Create(workspaceXml, composition: s_mockComposition); var document = workspace.Documents.First(); var updateArgs = DiagnosticsUpdatedArgs.DiagnosticsCreated( - new object(), workspace, workspace.CurrentSolution, document.Project.Id, document.Id, - ImmutableArray.Create( - TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 0)), - TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 1)))); + new object(), workspace, workspace.CurrentSolution, document.Project.Id, document.Id, + ImmutableArray.Create( + TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 0)), + TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 1)))); var spans = await TestDiagnosticTagProducer.GetErrorsFromUpdateSource(workspace, updateArgs); - Assert.Equal(1, spans.Count()); + Assert.Equal(2, spans.Count()); var first = spans.First(); + var second = spans.Last(); Assert.Equal(1, first.Span.Span.Length); + Assert.Equal(1, second.Span.Span.Length); } [WpfFact, Trait(Traits.Feature, Traits.Features.ErrorSquiggles)] @@ -324,7 +329,7 @@ class Test "; - using var workspace = TestWorkspace.Create(workspaceXml); + using var workspace = TestWorkspace.Create(workspaceXml, composition: s_mockComposition); var document = workspace.Documents.First(); var updateArgs = DiagnosticsUpdatedArgs.DiagnosticsCreated( diff --git a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs index 8ba38e4d6a313..29572be8f02a0 100644 --- a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs +++ b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs @@ -67,7 +67,7 @@ internal void CreateDiagnosticAndFireEvents(Workspace workspace, MockDiagnosticA location: location), document); - analyzerService.DiagnosticData = _diagnosticData; + analyzerService.Diagnostics = ImmutableArray.Create(_diagnosticData); DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( this, workspace, workspace.CurrentSolution, GetProjectId(workspace), GetDocumentId(workspace), diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 2422636153080..bf4ee518534b9 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Composition; using System.Linq; using System.Text; using System.Threading; @@ -18,9 +17,8 @@ using Microsoft.CodeAnalysis.EditAndContinue.Contracts; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests; +using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; diff --git a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs similarity index 94% rename from src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs rename to src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index f0d6b4283813f..566e196de223c 100644 --- a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -15,13 +15,13 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests +namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { [Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] internal class MockDiagnosticAnalyzerService : IDiagnosticAnalyzerService { public readonly List DocumentsToReanalyze = new(); - public DiagnosticData? DiagnosticData; + public ImmutableArray Diagnostics; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -54,7 +54,7 @@ public Task> GetDiagnosticsForIdsAsync(Solution s => throw new NotImplementedException(); public Task> GetDiagnosticsForSpanAsync(Document document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, bool includeSuppressedDiagnostics = true, CodeActionRequestPriority priority = CodeActionRequestPriority.None, Func? addOperationScope = null, CancellationToken cancellationToken = default) - => DiagnosticData != null ? Task.FromResult(ImmutableArray.Create(DiagnosticData)) : throw new NotImplementedException(); + => !Diagnostics.IsDefault ? Task.FromResult(Diagnostics) : throw new NotImplementedException(); public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) => throw new NotImplementedException(); diff --git a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs index 0ea6f9dd2aa19..205794045d2c8 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs @@ -42,6 +42,9 @@ internal static async Task>> GetErrorsFromUpdateSource(Test var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); using var disposable = (IDisposable)tagger; + var analyzerServer = (MockDiagnosticAnalyzerService)workspace.GetService(); + analyzerServer.Diagnostics = updateArgs.GetAllDiagnosticsRegardlessOfPushPullSetting(); + source.RaiseDiagnosticsUpdated(updateArgs); await wrapper.WaitForTags(); From 6adec2018e31cf42dc850c25e709faf37799a099 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Sep 2022 13:50:06 -0700 Subject: [PATCH 6/8] Update test --- .../EditAndContinue/RemoteEditAndContinueServiceTests.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index bf4ee518534b9..7d64e15eeafa4 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -41,10 +41,12 @@ private static string Inspect(DiagnosticData d) [Theory, CombinatorialData] public async Task Proxy(TestHost testHost) { - var localComposition = EditorTestCompositions.EditorFeatures.WithTestHostParts(testHost); + var localComposition = EditorTestCompositions.EditorFeatures.WithTestHostParts(testHost) + .AddExcludedPartTypes(typeof(DiagnosticAnalyzerService)) + .AddParts(typeof(MockDiagnosticAnalyzerService)); if (testHost == TestHost.InProcess) { - localComposition = localComposition.AddParts(typeof(MockEditAndContinueWorkspaceService), typeof(MockDiagnosticAnalyzerService)); + localComposition = localComposition.AddParts(typeof(MockEditAndContinueWorkspaceService)); } using var localWorkspace = new TestWorkspace(composition: localComposition); From 3ea2e164e751b8a0d66150e1d4c28bac59de8db9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Sep 2022 16:56:27 -0700 Subject: [PATCH 7/8] fixup --- .../AbstractDocumentDiagnosticSource.cs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs index 462969aa2ed8b..49189fd47d5cb 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.TodoComments; @@ -51,15 +52,21 @@ private async Task> GetTodoCommentDiagnosticsAsyn if (this.Document is not Document document) return ImmutableArray.Empty; - var service = document.GetLanguageService(); + var service = document.GetLanguageService(); if (service == null) return ImmutableArray.Empty; var tokenList = document.Project.Solution.Options.GetOption(TodoCommentOptionsStorage.TokenList); var descriptors = GetAndCacheDescriptors(tokenList); - var comments = await service.GetTodoCommentDataAsync(document, descriptors, cancellationToken).ConfigureAwait(false); - return comments.SelectAsArray(comment => new DiagnosticData( + var comments = await service.GetTodoCommentsAsync(document, descriptors, cancellationToken).ConfigureAwait(false); + if (comments.Length == 0) + return ImmutableArray.Empty; + + using var _ = ArrayBuilder.GetInstance(out var converted); + await TodoComment.ConvertAsync(document, comments, converted, cancellationToken).ConfigureAwait(false); + + return converted.SelectAsArray(comment => new DiagnosticData( id: "TODO", category: "TODO", message: comment.Message, @@ -73,16 +80,16 @@ private async Task> GetTodoCommentDiagnosticsAsyn language: document.Project.Language, location: new DiagnosticDataLocation( document.Id, - originalFilePath: comment.Span.Path, - mappedFilePath: comment.MappedSpan.Path, - originalStartLine: comment.Span.StartLinePosition.Line, - originalStartColumn: comment.Span.StartLinePosition.Character, - originalEndLine: comment.Span.EndLinePosition.Line, - originalEndColumn: comment.Span.EndLinePosition.Character, - mappedStartLine: comment.MappedSpan.StartLinePosition.Line, - mappedStartColumn: comment.MappedSpan.StartLinePosition.Character, - mappedEndLine: comment.MappedSpan.EndLinePosition.Line, - mappedEndColumn: comment.MappedSpan.EndLinePosition.Character))); + originalFilePath: comment.OriginalFilePath, + mappedFilePath: comment.MappedFilePath, + originalStartLine: comment.OriginalLine, + originalStartColumn: comment.OriginalColumn, + originalEndLine: comment.OriginalLine, + originalEndColumn: comment.OriginalColumn, + mappedStartLine: comment.MappedLine, + mappedStartColumn: comment.MappedColumn, + mappedEndLine: comment.MappedLine, + mappedEndColumn: comment.MappedColumn))); } private static ImmutableArray GetAndCacheDescriptors(ImmutableArray tokenList) From 15ff65eb84ccb481c57d29bb609dbcd9b8218c7d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Sep 2022 17:07:12 -0700 Subject: [PATCH 8/8] Fix --- .../CSharpTest/TodoComment/NoCompilationTodoCommentTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EditorFeatures/CSharpTest/TodoComment/NoCompilationTodoCommentTests.cs b/src/EditorFeatures/CSharpTest/TodoComment/NoCompilationTodoCommentTests.cs index c473e9c2e577f..a378b93eec6c0 100644 --- a/src/EditorFeatures/CSharpTest/TodoComment/NoCompilationTodoCommentTests.cs +++ b/src/EditorFeatures/CSharpTest/TodoComment/NoCompilationTodoCommentTests.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Test.Utilities;