Skip to content

Commit

Permalink
Merged PR 31574: Merge main with analyzer fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
arunchndr committed May 26, 2023
1 parent 8cc1f83 commit b7caf7f
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
Expand Down Expand Up @@ -588,5 +590,49 @@ public void TestCancellationDuringSuppressorExecution(bool concurrent)
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
Assert.Throws<OperationCanceledException>(() => compilation.GetAnalyzerDiagnostics(analyzersAndSuppressors, reportSuppressedDiagnostics: true, cancellationToken: cancellationToken));
}

[Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1819603")]
public async Task TestSuppressionAcrossCallsIntoCompilationWithAnalyzers(bool withSuppressor)
{
string source = @"class C1 { }";
var compilation = CreateCompilation(new[] { source });
compilation.VerifyDiagnostics();

// First verify analyzer diagnostics without any suppressors
var analyzer1 = new SemanticModelAnalyzerWithId("ID0001");
var analyzer2 = new SemanticModelAnalyzerWithId("ID0002");
var expectedDiagnostics = new DiagnosticDescription[]
{
Diagnostic(analyzer1.Descriptor.Id, source, isSuppressed: false).WithLocation(1, 1),
Diagnostic(analyzer2.Descriptor.Id, source, isSuppressed: false).WithLocation(1, 1),
};

VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer1, analyzer2 }, expectedDiagnostics);

// Verify whole compilation analyzer diagnostics including suppressors.
var suppressor = new DiagnosticSuppressorForMultipleIds(analyzer1.Descriptor.Id, analyzer2.Descriptor.Id);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer1, analyzer2, suppressor };
expectedDiagnostics = new DiagnosticDescription[] {
Diagnostic(analyzer1.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1),
Diagnostic(analyzer2.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1),
};

VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors, expectedDiagnostics);
VerifySuppressedAndFilteredDiagnostics(compilation, analyzersAndSuppressors);

// Now, verify single file analyzer diagnostics with multiple calls using subset of analyzers.
var options = new CompilationWithAnalyzersOptions(AnalyzerOptions.Empty, onAnalyzerException: null, concurrentAnalysis: true, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: true);
var compilationWithAnalyzers = new CompilationWithAnalyzers(compilation, analyzersAndSuppressors.ToImmutableArray(), options);
var tree = compilation.SyntaxTrees[0];
var semanticModel = compilation.GetSemanticModel(tree);
var analyzers = withSuppressor ? ImmutableArray.Create<DiagnosticAnalyzer>(analyzer1, suppressor) : ImmutableArray.Create<DiagnosticAnalyzer>(analyzer1);
var diagnostics1 = await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, filterSpan: null, analyzers, CancellationToken.None);
diagnostics1.Verify(
Diagnostic(analyzer1.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1));
analyzers = withSuppressor ? ImmutableArray.Create<DiagnosticAnalyzer>(analyzer2, suppressor) : ImmutableArray.Create<DiagnosticAnalyzer>(analyzer2);
var diagnostics2 = await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, filterSpan: null, analyzers, CancellationToken.None);
diagnostics2.Verify(
Diagnostic(analyzer2.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class CompilationWithAnalyzers
private readonly Compilation _compilation;
private readonly AnalysisScope _compilationAnalysisScope;
private readonly ImmutableArray<DiagnosticAnalyzer> _analyzers;
private readonly ImmutableArray<DiagnosticSuppressor> _suppressors;
private readonly CompilationWithAnalyzersOptions _analysisOptions;
private readonly CancellationToken _cancellationToken;

Expand Down Expand Up @@ -95,6 +96,7 @@ private CompilationWithAnalyzers(Compilation compilation, ImmutableArray<Diagnos
.WithEventQueue(new AsyncQueue<CompilationEvent>());
_compilation = compilation;
_analyzers = analyzers;
_suppressors = analyzers.OfType<DiagnosticSuppressor>().ToImmutableArrayOrEmpty();
_analysisOptions = analysisOptions;
_cancellationToken = cancellationToken;

Expand Down Expand Up @@ -296,7 +298,7 @@ async Task<ImmutableArray<Diagnostic>> getAllDiagnosticsWithoutStateTrackingAsyn

// Create and attach the driver to compilation.
var analysisScope = AnalysisScope.Create(compilation, analyzers, this);
using var driver = await CreateAndInitializeDriverAsync(compilation, _analysisOptions, analysisScope, categorizeDiagnostics: false, cancellationToken).ConfigureAwait(false);
using var driver = await CreateAndInitializeDriverAsync(compilation, _analysisOptions, analysisScope, _suppressors, categorizeDiagnostics: false, cancellationToken).ConfigureAwait(false);
driver.AttachQueueAndStartProcessingEvents(compilation.EventQueue!, analysisScope, usingPrePopulatedEventQueue: false, cancellationToken);

// Force compilation diagnostics and wait for analyzer execution to complete.
Expand Down Expand Up @@ -354,10 +356,20 @@ private static async Task<AnalyzerDriver> CreateAndInitializeDriverAsync(
Compilation compilation,
CompilationWithAnalyzersOptions analysisOptions,
AnalysisScope analysisScope,
ImmutableArray<DiagnosticSuppressor> suppressors,
bool categorizeDiagnostics,
CancellationToken cancellationToken)
{
var driver = compilation.CreateAnalyzerDriver(analysisScope.Analyzers, new AnalyzerManager(analysisScope.Analyzers), severityFilter: SeverityFilter.None);
var analyzers = analysisScope.Analyzers;
if (!suppressors.IsEmpty)
{
// Always provide all the diagnostic suppressors to the driver.
// We also need to ensure we are not passing any duplicate suppressor instances.
var suppressorsInAnalysisScope = analysisScope.Analyzers.OfType<DiagnosticSuppressor>().ToImmutableHashSet();
analyzers = analyzers.AddRange(suppressors.Where(suppressor => !suppressorsInAnalysisScope.Contains(suppressor)));
}

var driver = compilation.CreateAnalyzerDriver(analyzers, new AnalyzerManager(analyzers), severityFilter: SeverityFilter.None);
driver.Initialize(compilation, analysisOptions, new CompilationData(compilation), analysisScope, categorizeDiagnostics, trackSuppressedDiagnosticIds: false, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
await driver.WhenInitializedTask.ConfigureAwait(false);
Expand Down Expand Up @@ -672,7 +684,7 @@ private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope? analysisScope,
: _compilation.WithSemanticModelProvider(new CachingSemanticModelProvider()).WithEventQueue(new AsyncQueue<CompilationEvent>());

// Get the analyzer driver to execute analysis.
using var driver = await CreateAndInitializeDriverAsync(compilation, _analysisOptions, analysisScope, categorizeDiagnostics: true, cancellationToken).ConfigureAwait(false);
using var driver = await CreateAndInitializeDriverAsync(compilation, _analysisOptions, analysisScope, _suppressors, categorizeDiagnostics: true, cancellationToken).ConfigureAwait(false);

// Driver must have been initialized.
Debug.Assert(driver.IsInitialized);
Expand Down Expand Up @@ -1215,7 +1227,7 @@ public async Task<AnalyzerTelemetryInfo> GetAnalyzerTelemetryInfoAsync(Diagnosti
private async Task<AnalyzerActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
var analysisScope = _compilationAnalysisScope.WithAnalyzers(ImmutableArray.Create(analyzer), this);
using var driver = await CreateAndInitializeDriverAsync(_compilation, _analysisOptions, analysisScope, categorizeDiagnostics: true, cancellationToken).ConfigureAwait(false);
using var driver = await CreateAndInitializeDriverAsync(_compilation, _analysisOptions, analysisScope, _suppressors, categorizeDiagnostics: true, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
return await driver.GetAnalyzerActionCountsAsync(analyzer, _compilation.Options, analysisScope, cancellationToken).ConfigureAwait(false);
}
Expand Down
60 changes: 60 additions & 0 deletions src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,30 @@ private void OnCompilation(CompilationAnalysisContext context)
}
}

[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class SemanticModelAnalyzerWithId : DiagnosticAnalyzer
{
public SemanticModelAnalyzerWithId(string diagnosticId)
{
Descriptor = new DiagnosticDescriptor(
diagnosticId,
"Description1",
string.Empty,
"Analysis",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
}

public DiagnosticDescriptor Descriptor { get; }

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);

public override void Initialize(AnalysisContext context) =>
context.RegisterSemanticModelAction(context =>
context.ReportDiagnostic(
Diagnostic.Create(Descriptor, context.SemanticModel.SyntaxTree.GetRoot().GetLocation())));
}

/// <summary>
/// This analyzer is intended to be used only when concurrent execution is enabled for analyzers.
/// This analyzer will deadlock if the driver runs analyzers on a single thread OR takes a lock around callbacks into this analyzer to prevent concurrent analyzer execution
Expand Down Expand Up @@ -2068,6 +2092,42 @@ public override void ReportSuppressions(SuppressionAnalysisContext context)
}
}

[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressorForMultipleIds : DiagnosticSuppressor
{
public DiagnosticSuppressorForMultipleIds(params string[] suppressedDiagnosticIds)
{
var builder = ImmutableArray.CreateBuilder<SuppressionDescriptor>();
int i = 1;
foreach (var suppressedDiagnosticId in suppressedDiagnosticIds)
{
var descriptor = new SuppressionDescriptor(
id: $"SPR000{i++}",
suppressedDiagnosticId: suppressedDiagnosticId,
justification: $"Suppress {suppressedDiagnosticId}");
builder.Add(descriptor);
}

SupportedSuppressions = builder.ToImmutable();
}

public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions { get; }

public override void ReportSuppressions(SuppressionAnalysisContext context)
{
foreach (var diagnostic in context.ReportedDiagnostics)
{
foreach (var suppressionDescriptor in SupportedSuppressions)
{
if (suppressionDescriptor.SuppressedDiagnosticId == diagnostic.Id)
{
context.ReportSuppression(Suppression.Create(suppressionDescriptor, diagnostic));
}
}
}
}
}

[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressorForId_ThrowsOperationCancelledException : DiagnosticSuppressor
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
{
Logger.Log(FunctionId.Rename_InlineSession_Cancel_NonDocumentChangedWorkspaceChange, KeyValueLogMessage.Create(m =>
{
m["Kind"] = Enum.GetName(typeof(WorkspaceChangeEventArgs), args.Kind);
m["Kind"] = Enum.GetName(typeof(WorkspaceChangeKind), args.Kind);
}));

Cancel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public override async Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(
// Also ensure we pass in "includeSuppressedDiagnostics = true" for unnecessary suppressions to be reported.
var allSpanDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForSpanAsync(
Document, range: null, diagnosticKind: this.DiagnosticKind, includeSuppressedDiagnostics: true, cancellationToken: cancellationToken).ConfigureAwait(false);

// Drop the source suppressed diagnostics.
// https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1824321 tracks
// adding LSP support for returning source suppressed diagnostics.
allSpanDiagnostics = allSpanDiagnostics.WhereAsArray(diagnostic => !diagnostic.IsSuppressed);

return allSpanDiagnostics;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ void M()
}

[Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1806590")]
public async Task TestDocumentDiagnosticsDiagnosticsForUnnecessarySuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace)
public async Task TestDocumentDiagnosticsForUnnecessarySuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace)
{
var markup = "#pragma warning disable IDE0000";
await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: true);
Expand All @@ -817,6 +817,31 @@ public async Task TestDocumentDiagnosticsDiagnosticsForUnnecessarySuppressions(b
Assert.Equal(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId, results.Single().Diagnostics.Single().Code);
}

[Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1824321")]
public async Task TestDocumentDiagnosticsForSourceSuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace)
{
var markup = @"
class C
{
void M()
{
#pragma warning disable CS0168 // Variable is declared but never used
int x;
#pragma warning restore CS0168 // Variable is declared but never used
}
}";
await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: true);

var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single();

await OpenDocumentAsync(testLspServer, document);

var results = await RunGetDocumentPullDiagnosticsAsync(
testLspServer, document.GetURI(), useVSDiagnostics);

Assert.Empty(results.Single().Diagnostics);
}

#endregion

#region Workspace Diagnostics
Expand Down

0 comments on commit b7caf7f

Please sign in to comment.