未验证 提交 d1532d6e 编写于 作者: M msftbot[bot] 提交者: GitHub

Merge pull request #44817 from CyrusNajmabadi/unnecessaryParenFading

Fix fading locations for unnecessary code.
......@@ -121,8 +121,10 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
var severity = preference.Notification.Severity;
var additionalLocations = ImmutableArray.Create(parenthesizedExpression.GetLocation(),
parenthesizedExpression.GetFirstToken().GetLocation(), parenthesizedExpression.GetLastToken().GetLocation());
var additionalLocations = ImmutableArray.Create(
parenthesizedExpression.GetLocation(),
parenthesizedExpression.GetFirstToken().GetLocation(),
parenthesizedExpression.GetLastToken().GetLocation());
context.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags(
s_diagnosticDescriptor,
......
......@@ -230,7 +230,7 @@ public async Task SemanticErrorReported()
public async Task TestNoErrorsAfterDocumentRemoved()
{
using var workspace = TestWorkspace.CreateCSharp("class");
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider>(workspace);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace);
var tagger = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
using var disposable = tagger as IDisposable;
await wrapper.WaitForTags();
......@@ -256,7 +256,7 @@ public async Task TestNoErrorsAfterDocumentRemoved()
public async Task TestNoErrorsAfterProjectRemoved()
{
using var workspace = TestWorkspace.CreateCSharp("class");
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider>(workspace);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace);
var tagger = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
using var disposable = tagger as IDisposable;
await wrapper.WaitForTags();
......
......@@ -60,13 +60,14 @@ internal partial class DiagnosticsClassificationTaggerProvider : AbstractDiagnos
// If we are under high contrast mode, the editor ignores classification tags that fade things out,
// because that reduces contrast. Since the editor will ignore them, there's no reason to produce them.
protected internal override bool IsEnabled => !_editorOptionsFactoryService.GlobalOptions.GetOptionValue(DefaultTextViewHostOptions.IsInContrastModeId);
protected internal override bool IsEnabled
=> !_editorOptionsFactoryService.GlobalOptions.GetOptionValue(DefaultTextViewHostOptions.IsInContrastModeId);
protected internal override bool IncludeDiagnostic(DiagnosticData data) =>
data.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary);
protected internal override bool IncludeDiagnostic(DiagnosticData data)
=> data.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary);
protected internal override ITagSpan<ClassificationTag> CreateTagSpan(Workspace workspace, bool isLiveUpdate, SnapshotSpan span, DiagnosticData data) =>
new TagSpan<ClassificationTag>(span, _classificationTag);
protected internal override ITagSpan<ClassificationTag> CreateTagSpan(Workspace workspace, bool isLiveUpdate, SnapshotSpan span, DiagnosticData data)
=> new TagSpan<ClassificationTag>(span, _classificationTag);
protected internal override ImmutableArray<DiagnosticDataLocation> GetLocationsToTag(DiagnosticData diagnosticData)
{
......@@ -76,24 +77,26 @@ protected internal override ImmutableArray<DiagnosticDataLocation> GetLocationsT
&& diagnosticData.Properties.TryGetValue(WellKnownDiagnosticTags.Unnecessary, out var unnecessaryIndices)
&& unnecessaryIndices is object)
{
using var locationsToTagDisposer = PooledObjects.ArrayBuilder<DiagnosticDataLocation>.GetInstance(out var locationsToTag);
using var _ = PooledObjects.ArrayBuilder<DiagnosticDataLocation>.GetInstance(out var locationsToTag);
var additionalLocations = diagnosticData.AdditionalLocations.ToImmutableArray();
var indices = GetLocationIndices(unnecessaryIndices);
locationsToTag.AddRange(indices.Select(i => additionalLocations[i]).ToImmutableArray());
foreach (var index in GetLocationIndices(unnecessaryIndices))
locationsToTag.Add(additionalLocations[index]);
return locationsToTag.ToImmutable();
}
// Default to the base implementation for the diagnostic data
return base.GetLocationsToTag(diagnosticData);
static IEnumerable<int>? GetLocationIndices(string indicesProperty)
static IEnumerable<int> GetLocationIndices(string indicesProperty)
{
try
{
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(indicesProperty));
var serializer = new DataContractJsonSerializer(typeof(IEnumerable<int>));
var result = serializer.ReadObject(stream) as IEnumerable<int>;
return result;
return result ?? Array.Empty<int>();
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e))
{
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
{
[UseExportProvider]
public class DiagnosticsClassificationTaggerProviderTests
{
[WpfFact, Trait(Traits.Feature, Traits.Features.Diagnostics)]
public async Task Test_FadingSpans()
{
var analyzer = new Analyzer();
var analyzerMap = new Dictionary<string, ImmutableArray<DiagnosticAnalyzer>>
{
{ LanguageNames.CSharp, ImmutableArray.Create<DiagnosticAnalyzer>(analyzer) }
};
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsClassificationTaggerProvider, ClassificationTag>(workspace, analyzerMap);
var tagger = wrapper.TaggerProvider.CreateTagger<ClassificationTag>(workspace.Documents.First().GetTextBuffer());
using var disposable = tagger as IDisposable;
// test first update
await wrapper.WaitForTags();
// We should get two spans, the 1-index and 2-index locations in the original diagnostic.
var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot;
var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList();
Assert.Equal(2, spans.Count);
Assert.Equal(new Span(0, 1), spans[0].Span.Span);
Assert.Equal(new Span(9, 1), spans[1].Span.Span);
Assert.Equal(ClassificationTypeDefinitions.UnnecessaryCode, spans[0].Tag.ClassificationType.Classification);
Assert.Equal(ClassificationTypeDefinitions.UnnecessaryCode, spans[1].Tag.ClassificationType.Classification);
}
private class Analyzer : DiagnosticAnalyzer
{
// Mark elements 1, and 2 in the locations as the fading locations.
private static readonly ImmutableDictionary<string, IEnumerable<int>> s_fadeLocations = new Dictionary<string, IEnumerable<int>>
{
{ nameof(WellKnownDiagnosticTags.Unnecessary), new int[] { 1, 2 } },
}.ToImmutableDictionary();
private readonly DiagnosticDescriptor _rule = new DiagnosticDescriptor(
"test", "test", "test", "test", DiagnosticSeverity.Error, true,
customTags: DiagnosticCustomTags.Create(isUnnecessary: true, isConfigurable: false));
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(_rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxTreeAction(c =>
{
c.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags(
_rule, Location.Create(c.Tree, new TextSpan(0, 10)),
ReportDiagnostic.Error,
new Location[] {
Location.Create(c.Tree, new TextSpan(0, 10)),
Location.Create(c.Tree, new TextSpan(0, 1)),
Location.Create(c.Tree, new TextSpan(9, 1)),
},
s_fadeLocations));
});
}
}
}
}
......@@ -10,7 +10,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics;
......@@ -42,7 +41,7 @@ public async Task Test_TagSourceDiffer()
};
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider>(workspace, analyzerMap);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace, analyzerMap);
var tagger = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
using var disposable = tagger as IDisposable;
// test first update
......@@ -70,7 +69,7 @@ public async Task Test_TagSourceDiffer()
public async Task MultipleTaggersAndDispose()
{
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A {" }, CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider>(workspace);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace);
// Make two taggers.
var tagger1 = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
var tagger2 = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
......@@ -90,7 +89,7 @@ public async Task MultipleTaggersAndDispose()
public async Task TaggerProviderCreatedAfterInitialDiagnosticsReported()
{
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class C {" }, CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider>(workspace, analyzerMap: null, createTaggerProvider: false);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace, analyzerMap: null, createTaggerProvider: false);
// First, make sure all diagnostics have been reported.
await wrapper.WaitForTags();
......@@ -118,7 +117,7 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedBeforeIniti
// diagnostics engine not actually reporting all diagnostics properly.
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }" }, CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider>(workspace);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace);
var listenerProvider = workspace.ExportProvider.GetExportedValue<IAsynchronousOperationListenerProvider>();
var diagnosticService = new MockDiagnosticService(workspace);
......@@ -156,7 +155,7 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedAfterInitia
// diagnostics engine not actually reporting all diagnostics properly.
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }" }, CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider>(workspace);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace);
var listenerProvider = workspace.ExportProvider.GetExportedValue<IAsynchronousOperationListenerProvider>();
var diagnosticService = new MockDiagnosticService(workspace);
......@@ -182,70 +181,6 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedAfterInitia
Assert.Equal(span.ToSpan(), spans[0].Span.Span);
}
private class MockDiagnosticService : IDiagnosticService
{
public const string DiagnosticId = "MockId";
private readonly Workspace _workspace;
private DiagnosticData? _diagnostic;
public event EventHandler<DiagnosticsUpdatedArgs>? DiagnosticsUpdated;
public MockDiagnosticService(Workspace workspace)
=> _workspace = workspace;
public IEnumerable<DiagnosticData> GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken)
{
Assert.Equal(workspace, _workspace);
Assert.Equal(projectId, GetProjectId());
Assert.Equal(documentId, GetDocumentId());
if (_diagnostic == null)
{
yield break;
}
else
{
yield return _diagnostic;
}
}
public IEnumerable<UpdatedEventArgs> GetDiagnosticsUpdatedEventArgs(Workspace workspace, ProjectId projectId, DocumentId documentId, CancellationToken cancellationToken)
{
Assert.Equal(workspace, _workspace);
Assert.Equal(projectId, GetProjectId());
Assert.Equal(documentId, GetDocumentId());
if (_diagnostic == null)
{
yield break;
}
else
{
yield return new UpdatedEventArgs(this, workspace, GetProjectId(), GetDocumentId());
}
}
internal void CreateDiagnosticAndFireEvents(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,
location: location),
document);
DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated(
this, _workspace, _workspace.CurrentSolution,
GetProjectId(), GetDocumentId(),
ImmutableArray.Create(_diagnostic)));
}
private DocumentId GetDocumentId()
=> _workspace.CurrentSolution.Projects.Single().Documents.Single().Id;
private ProjectId GetProjectId()
=> _workspace.CurrentSolution.Projects.Single().Id;
}
private class Analyzer : DiagnosticAnalyzer
{
private DiagnosticDescriptor _rule = new DiagnosticDescriptor("test", "test", "test", "test", DiagnosticSeverity.Error, true);
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
{
internal class MockDiagnosticService : IDiagnosticService
{
public const string DiagnosticId = "MockId";
private readonly Workspace _workspace;
private DiagnosticData? _diagnostic;
public event EventHandler<DiagnosticsUpdatedArgs>? DiagnosticsUpdated;
public MockDiagnosticService(Workspace workspace)
=> _workspace = workspace;
public IEnumerable<DiagnosticData> GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken)
{
Assert.Equal(workspace, _workspace);
Assert.Equal(projectId, GetProjectId());
Assert.Equal(documentId, GetDocumentId());
if (_diagnostic == null)
{
yield break;
}
else
{
yield return _diagnostic;
}
}
public IEnumerable<UpdatedEventArgs> GetDiagnosticsUpdatedEventArgs(Workspace workspace, ProjectId projectId, DocumentId documentId, CancellationToken cancellationToken)
{
Assert.Equal(workspace, _workspace);
Assert.Equal(projectId, GetProjectId());
Assert.Equal(documentId, GetDocumentId());
if (_diagnostic == null)
{
yield break;
}
else
{
yield return new UpdatedEventArgs(this, workspace, GetProjectId(), GetDocumentId());
}
}
internal void CreateDiagnosticAndFireEvents(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,
location: location),
document);
DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated(
this, _workspace, _workspace.CurrentSolution,
GetProjectId(), GetDocumentId(),
ImmutableArray.Create(_diagnostic)));
}
private DocumentId GetDocumentId()
=> _workspace.CurrentSolution.Projects.Single().Documents.Single().Id;
private ProjectId GetProjectId()
=> _workspace.CurrentSolution.Projects.Single().Id;
}
}
......@@ -7,12 +7,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.VisualStudio.Text.Tagging;
......@@ -20,8 +20,9 @@
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
{
internal class DiagnosticTaggerWrapper<TProvider> : IDisposable
where TProvider : AbstractDiagnosticsAdornmentTaggerProvider<IErrorTag>
internal class DiagnosticTaggerWrapper<TProvider, TTag> : IDisposable
where TProvider : AbstractDiagnosticsTaggerProvider<TTag>
where TTag : ITag
{
private readonly TestWorkspace _workspace;
public readonly DiagnosticAnalyzerService? AnalyzerService;
......@@ -56,7 +57,7 @@ internal class DiagnosticTaggerWrapper<TProvider> : IDisposable
_registrationService = workspace.Services.GetRequiredService<ISolutionCrawlerRegistrationService>();
_registrationService.Register(workspace);
DiagnosticService = new DiagnosticService(_listenerProvider, Array.Empty<Lazy<IEventListener, EventListenerMetadata>>());
DiagnosticService = (DiagnosticService)workspace.ExportProvider.GetExportedValue<IDiagnosticService>();
DiagnosticService.Register(updateSource);
if (createTaggerProvider)
......@@ -77,7 +78,7 @@ public ITaggerProvider TaggerProvider
{
if (_taggerProvider == null)
{
WpfTestRunner.RequireWpfFact($"{nameof(DiagnosticTaggerWrapper<TProvider>)}.{nameof(TaggerProvider)} creates asynchronous taggers");
WpfTestRunner.RequireWpfFact($"{nameof(DiagnosticTaggerWrapper<TProvider, TTag>)}.{nameof(TaggerProvider)} creates asynchronous taggers");
if (typeof(TProvider) == typeof(DiagnosticsSquiggleTaggerProvider))
{
......@@ -95,6 +96,12 @@ public ITaggerProvider TaggerProvider
_workspace.GetService<IForegroundNotificationService>(),
_listenerProvider);
}
else if (typeof(TProvider) == typeof(DiagnosticsClassificationTaggerProvider))
{
_taggerProvider = _workspace.ExportProvider.GetExportedValues<ITaggerProvider>()
.OfType<DiagnosticsClassificationTaggerProvider>()
.Single();
}
else
{
throw new InvalidOperationException();
......@@ -117,6 +124,7 @@ public async Task WaitForTags()
await _listenerProvider.GetWaiter(FeatureAttribute.DiagnosticService).ExpeditedWaitAsync();
await _listenerProvider.GetWaiter(FeatureAttribute.ErrorSquiggles).ExpeditedWaitAsync();
await _listenerProvider.GetWaiter(FeatureAttribute.Classification).ExpeditedWaitAsync();
}
private class MyDiagnosticAnalyzerService : DiagnosticAnalyzerService
......
......@@ -26,21 +26,18 @@ public static class SquiggleUtilities
IReadOnlyDictionary<string, ImmutableArray<DiagnosticAnalyzer>> analyzerMap = null)
where TProvider : AbstractDiagnosticsAdornmentTaggerProvider<IErrorTag>
{
using (var wrapper = new DiagnosticTaggerWrapper<TProvider>(workspace, analyzerMap))
{
var tagger = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
using (var disposable = tagger as IDisposable)
{
await wrapper.WaitForTags();
using var wrapper = new DiagnosticTaggerWrapper<TProvider, IErrorTag>(workspace, analyzerMap);
var tagger = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
var analyzerDiagnostics = await wrapper.AnalyzerService.GetDiagnosticsAsync(workspace.CurrentSolution);
using var disposable = tagger as IDisposable;
await wrapper.WaitForTags();
var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot;
var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToImmutableArray();
var analyzerDiagnostics = await wrapper.AnalyzerService.GetDiagnosticsAsync(workspace.CurrentSolution);
return (analyzerDiagnostics, spans);
}
}
var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot;
var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToImmutableArray();
return (analyzerDiagnostics, spans);
}
}
......@@ -57,7 +54,7 @@ internal sealed class DiagnosticTagProducer<TProvider>
internal async Task<IList<ITagSpan<IErrorTag>>> GetErrorsFromUpdateSource(TestWorkspace workspace, DiagnosticsUpdatedArgs updateArgs)
{
var source = new TestDiagnosticUpdateSource();
using (var wrapper = new DiagnosticTaggerWrapper<TProvider>(workspace, updateSource: source))
using (var wrapper = new DiagnosticTaggerWrapper<TProvider, IErrorTag>(workspace, updateSource: source))
{
var tagger = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
using (var disposable = tagger as IDisposable)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册