diff --git a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj index 93dad6ec222a3..58201385dd378 100644 --- a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj +++ b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj @@ -28,6 +28,7 @@ + diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/CompilationWithAnalyzersTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/CompilationWithAnalyzersTests.cs new file mode 100644 index 0000000000000..c89a4925ec046 --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/CompilationWithAnalyzersTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Test.Utilities; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics +{ + using SimpleDiagnostic = Diagnostic.SimpleDiagnostic; + + public class CompilationWithAnalyzersTests : TestBase + { + [Fact] + public void GetEffectiveDiagnostics_Errors() + { + var c = CSharpCompilation.Create("c"); + var ds = new[] { default(Diagnostic) }; + + Assert.Throws(() => CompilationWithAnalyzers.GetEffectiveDiagnostics(default(ImmutableArray), c)); + Assert.Throws(() => CompilationWithAnalyzers.GetEffectiveDiagnostics(default(IEnumerable), c)); + Assert.Throws(() => CompilationWithAnalyzers.GetEffectiveDiagnostics(ds, null)); + } + + [Fact] + public void GetEffectiveDiagnostics() + { + var c = CSharpCompilation.Create("c", options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary). + WithSpecificDiagnosticOptions( + new[] { KeyValuePair.Create($"CS{(int)ErrorCode.WRN_AlwaysNull:D4}", ReportDiagnostic.Suppress) })); + + var d1 = SimpleDiagnostic.Create(MessageProvider.Instance, (int)ErrorCode.WRN_AlignmentMagnitude); + var d2 = SimpleDiagnostic.Create(MessageProvider.Instance, (int)ErrorCode.WRN_AlwaysNull); + var ds = new[] { default(Diagnostic), d1, d2 }; + + var filtered = CompilationWithAnalyzers.GetEffectiveDiagnostics(ds, c); + + // overwrite the original value to test eagerness: + ds[1] = default(Diagnostic); + + AssertEx.Equal(new[] { d1 }, filtered); + } + } +} diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index ae50a0f792ca7..e739a4c31a395 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -1010,8 +1010,18 @@ private static void FreeEventQueue(AsyncQueue eventQueue, Obje /// 4) Pragma directives for the given . /// public static IEnumerable GetEffectiveDiagnostics(IEnumerable diagnostics, Compilation compilation) + => GetEffectiveDiagnostics(diagnostics.AsImmutableOrNull(), compilation); + + /// + /// Given a set of compiler or generated , returns the effective diagnostics after applying the below filters: + /// 1) specified for the given . + /// 2) specified for the given . + /// 3) Diagnostic suppression through applied . + /// 4) Pragma directives for the given . + /// + public static IEnumerable GetEffectiveDiagnostics(ImmutableArray diagnostics, Compilation compilation) { - if (diagnostics == null) + if (diagnostics.IsDefault) { throw new ArgumentNullException(nameof(diagnostics)); } @@ -1021,16 +1031,20 @@ public static IEnumerable GetEffectiveDiagnostics(IEnumerable GetEffectiveDiagnosticsImpl(ImmutableArray diagnostics, Compilation compilation) + { var suppressMessageState = new SuppressMessageAttributeState(compilation); - foreach (var diagnostic in diagnostics.ToImmutableArray()) + foreach (var diagnostic in diagnostics) { if (diagnostic != null) { var effectiveDiagnostic = compilation.Options.FilterDiagnostic(diagnostic); if (effectiveDiagnostic != null) { - effectiveDiagnostic = suppressMessageState.ApplySourceSuppressions(effectiveDiagnostic); - yield return effectiveDiagnostic; + yield return suppressMessageState.ApplySourceSuppressions(effectiveDiagnostic); } } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index fc5b10b986d0c..681bd6232b7a6 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -738,6 +738,7 @@ override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitVariableDeclarati override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitWhileUntilLoopStatement(Microsoft.CodeAnalysis.Semantics.IWhileUntilLoopStatement operation) -> void override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitWithStatement(Microsoft.CodeAnalysis.Semantics.IWithStatement operation) -> void override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitYieldBreakStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation) -> void +static Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.GetEffectiveDiagnostics(System.Collections.Immutable.ImmutableArray diagnostics, Microsoft.CodeAnalysis.Compilation compilation) -> System.Collections.Generic.IEnumerable static Microsoft.CodeAnalysis.Semantics.OperationExtensions.Descendants(this Microsoft.CodeAnalysis.IOperation operation) -> System.Collections.Generic.IEnumerable static Microsoft.CodeAnalysis.Semantics.OperationExtensions.DescendantsAndSelf(this Microsoft.CodeAnalysis.IOperation operation) -> System.Collections.Generic.IEnumerable static Microsoft.CodeAnalysis.Semantics.OperationExtensions.GetRootOperation(this Microsoft.CodeAnalysis.ISymbol symbol, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.IOperation diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs index a1d4ad7b361f4..c3a8af723bb37 100644 --- a/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs +++ b/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs @@ -12,12 +12,22 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue { + internal sealed class EncErrorId : BuildToolId.Base + { + public EncErrorId(DebuggingSession session, object errorId) + : base(session, errorId) + { + } + + public override string BuildTool => PredefinedBuildTools.EnC; + } + [Export(typeof(EditAndContinueDiagnosticUpdateSource))] [Shared] internal sealed class EditAndContinueDiagnosticUpdateSource : IDiagnosticUpdateSource { - internal static object DebuggerErrorId = new object(); - internal static object EmitErrorId = new object(); + internal static readonly object DebuggerErrorId = new object(); + internal static readonly object EmitErrorId = new object(); [ImportingConstructor] public EditAndContinueDiagnosticUpdateSource(IDiagnosticUpdateSourceRegistrationService registrationService) @@ -25,7 +35,7 @@ public EditAndContinueDiagnosticUpdateSource(IDiagnosticUpdateSourceRegistration registrationService.Register(this); } - public bool SupportGetDiagnostics { get { return false; } } + public bool SupportGetDiagnostics => false; public event EventHandler DiagnosticsUpdated; @@ -34,100 +44,94 @@ public EditAndContinueDiagnosticUpdateSource(IDiagnosticUpdateSourceRegistration return ImmutableArray.Empty; } - public void ClearDiagnostics(DebuggingSession session, Workspace workspace, object kind, ProjectId projectId, ImmutableArray documentIds) + public void ClearDiagnostics(EncErrorId errorId, Solution solution, ProjectId projectId, ImmutableArray documentIds) { - if (documentIds.IsDefault) - { - return; - } + // clear project diagnostics: + ClearDiagnostics(errorId, solution, projectId, null); - foreach (var documentId in documentIds) + // clear document diagnostics: + foreach (var documentIdOpt in documentIds) { - ClearDiagnostics(session, workspace, kind, projectId, documentId); + ClearDiagnostics(errorId, solution, projectId, documentIdOpt); } } - public void ClearDiagnostics(DebuggingSession session, Workspace workspace, object errorId, ProjectId projectId, DocumentId documentId) + public void ClearDiagnostics(EncErrorId errorId, Solution solution, ProjectId projectId, DocumentId documentIdOpt) { - RaiseDiagnosticsUpdated(MakeRemovedArgs(session, workspace, errorId, projectId, documentId)); + DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsRemoved( + errorId, + solution.Workspace, + solution: solution, + projectId: projectId, + documentId: documentIdOpt)); } - public ImmutableArray ReportDiagnostics(DebuggingSession session, object errorId, ProjectId projectId, Solution solution, IEnumerable diagnostics) + public ImmutableArray ReportDiagnostics(object errorId, Solution solution, ProjectId projectId, IEnumerable diagnostics) { - var argsByDocument = ImmutableArray.CreateRange( - from diagnostic in diagnostics - let document = solution.GetDocument(diagnostic.Location.SourceTree, projectId) - where document != null - let item = MakeDiagnosticData(projectId, document, solution, diagnostic) - group item by document.Id into itemsByDocumentId - select MakeCreatedArgs(session, errorId, solution.Workspace, solution, projectId, itemsByDocumentId.Key, ImmutableArray.CreateRange(itemsByDocumentId))); - - foreach (var args in argsByDocument) - { - RaiseDiagnosticsUpdated(args); - } + Debug.Assert(errorId != null); + Debug.Assert(solution != null); + Debug.Assert(projectId != null); - return argsByDocument.SelectAsArray(args => args.DocumentId); - } + var updateEvent = DiagnosticsUpdated; + var documentIds = ArrayBuilder.GetInstance(); + var documentDiagnosticData = ArrayBuilder.GetInstance(); + var projectDiagnosticData = ArrayBuilder.GetInstance(); + var project = solution.GetProject(projectId); - private static DiagnosticData MakeDiagnosticData(ProjectId projectId, Document document, Solution solution, Diagnostic d) - { - if (document != null) + foreach (var diagnostic in diagnostics) { - return DiagnosticData.Create(document, d); + var documentOpt = solution.GetDocument(diagnostic.Location.SourceTree, projectId); + + if (documentOpt != null) + { + if (updateEvent != null) + { + documentDiagnosticData.Add(DiagnosticData.Create(documentOpt, diagnostic)); + } + + documentIds.Add(documentOpt.Id); + } + else if (updateEvent != null) + { + projectDiagnosticData.Add(DiagnosticData.Create(project, diagnostic)); + } } - else + + foreach (var documentDiagnostics in documentDiagnosticData.ToDictionary(data => data.DocumentId)) { - var project = solution.GetProject(projectId); - Debug.Assert(project != null); - return DiagnosticData.Create(project, d); + updateEvent(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( + errorId, + solution.Workspace, + solution, + projectId, + documentId: documentDiagnostics.Key, + diagnostics: documentDiagnostics.Value)); } - } - private DiagnosticsUpdatedArgs MakeCreatedArgs( - DebuggingSession session, Workspace workspace, object errorId, ProjectId projectId, DocumentId documentId, ImmutableArray items) - { - return MakeCreatedArgs(session, errorId, workspace, solution: null, projectId: projectId, documentId: documentId, items: items); - } - - private DiagnosticsUpdatedArgs MakeRemovedArgs( - DebuggingSession session, Workspace workspace, object errorId, ProjectId projectId, DocumentId documentId) - { - return MakeRemovedArgs(session, errorId, workspace, solution: null, projectId: projectId, documentId: documentId); - } + if (projectDiagnosticData.Count > 0) + { + updateEvent(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( + errorId, + solution.Workspace, + solution, + projectId, + documentId: null, + diagnostics: projectDiagnosticData.ToImmutable())); + } - private DiagnosticsUpdatedArgs MakeCreatedArgs( - DebuggingSession session, object errorId, Workspace workspace, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray items) - { - return DiagnosticsUpdatedArgs.DiagnosticsCreated( - CreateId(session, errorId), workspace, solution, projectId, documentId, items); + documentDiagnosticData.Free(); + projectDiagnosticData.Free(); + return documentIds.ToImmutableAndFree(); } - private DiagnosticsUpdatedArgs MakeRemovedArgs( - DebuggingSession session, object errorId, Workspace workspace, Solution solution, ProjectId projectId, DocumentId documentId) + internal ImmutableArray ReportDiagnostics(DebuggingSession session, object errorId, ProjectId projectId, Solution solution, IEnumerable diagnostics) { - return DiagnosticsUpdatedArgs.DiagnosticsRemoved( - CreateId(session, errorId), workspace, solution, projectId, documentId); + return ReportDiagnostics(new EncErrorId(session, errorId), solution, projectId, diagnostics); } - private static EnCId CreateId(DebuggingSession session, object errorId) => new EnCId(session, errorId); - - private void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args) + internal void ClearDiagnostics(DebuggingSession session, Workspace workspace, object errorId, ProjectId projectId, ImmutableArray documentIds) { - this.DiagnosticsUpdated?.Invoke(this, args); - } - - private class EnCId : BuildToolId.Base - { - public EnCId(DebuggingSession session, object errorId) : - base(session, errorId) - { - } - - public override string BuildTool - { - get { return PredefinedBuildTools.EnC; } - } + ClearDiagnostics(new EncErrorId(session, errorId), workspace.CurrentSolution, projectId, documentIds); } } } diff --git a/src/EditorFeatures/Test/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs b/src/EditorFeatures/Test/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs index 9a0749ce131e4..a098a3f7e8d67 100644 --- a/src/EditorFeatures/Test/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs +++ b/src/EditorFeatures/Test/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces { @@ -17,16 +18,15 @@ internal class NoCompilationDocumentDiagnosticAnalyzer : DocumentDiagnosticAnaly public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); - public override Task AnalyzeSemanticsAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken) + public override Task> AnalyzeSemanticsAsync(Document document, CancellationToken cancellationToken) { - return Task.FromResult(true); + return SpecializedTasks.EmptyImmutableArray(); } - public override Task AnalyzeSyntaxAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken) + public override Task> AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { - addDiagnostic(Diagnostic.Create(Descriptor, - Location.Create(document.FilePath, default(TextSpan), default(LinePositionSpan)))); - return Task.FromResult(true); + return Task.FromResult(ImmutableArray.Create( + Diagnostic.Create(Descriptor, Location.Create(document.FilePath, default(TextSpan), default(LinePositionSpan))))); } } } diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/IDocumentDiagnosticAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/IDocumentDiagnosticAnalyzer.cs index b1e24468ded7a..c3a0ec5d608fe 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/IDocumentDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/IDocumentDiagnosticAnalyzer.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { @@ -12,8 +14,33 @@ namespace Microsoft.CodeAnalysis.Diagnostics internal abstract class DocumentDiagnosticAnalyzer : DiagnosticAnalyzer { // REVIEW: why DocumentDiagnosticAnalyzer doesn't have span based analysis? - public abstract Task AnalyzeSyntaxAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken); - public abstract Task AnalyzeSemanticsAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken); + // TODO: Make abstract once TypeScript and F# move over to the overloads above + public async virtual Task> AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) + { + var builder = ArrayBuilder.GetInstance(); + await AnalyzeSyntaxAsync(document, builder.Add, cancellationToken).ConfigureAwait(false); + return builder.ToImmutableAndFree(); + } + + // TODO: Make abstract once TypeScript and F# move over to the overloads above + public async virtual Task> AnalyzeSemanticsAsync(Document document, CancellationToken cancellationToken) + { + var builder = ArrayBuilder.GetInstance(); + await AnalyzeSemanticsAsync(document, builder.Add, cancellationToken).ConfigureAwait(false); + return builder.ToImmutableAndFree(); + } + + // TODO: Remove once TypeScript and F# move over to the overloads above + public virtual Task AnalyzeSyntaxAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken) + { + throw ExceptionUtilities.Unreachable; + } + + // TODO: Remove once TypeScript and F# move over to the overloads above + public virtual Task AnalyzeSemanticsAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken) + { + throw ExceptionUtilities.Unreachable; + } /// /// it is not allowed one to implement both DocumentDiagnosticAnalzyer and DiagnosticAnalyzer diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/RudeEditUserDiagnosticAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/RudeEditUserDiagnosticAnalyzer.cs index 19e975c1d3bc4..1b47abb0db7d8 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/RudeEditUserDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/RudeEditUserDiagnosticAnalyzer.cs @@ -21,20 +21,20 @@ public override ImmutableArray SupportedDiagnostics } } - public override Task AnalyzeSyntaxAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken) + public override Task> AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { // No syntax diagnostics produced by the EnC engine. - return SpecializedTasks.EmptyTask; + return SpecializedTasks.EmptyImmutableArray(); } - public override async Task AnalyzeSemanticsAsync(Document document, Action addDiagnostic, CancellationToken cancellationToken) + public override async Task> AnalyzeSemanticsAsync(Document document, CancellationToken cancellationToken) { try { var encService = document.Project.Solution.Workspace.Services.GetService(); if (encService == null) { - return; + return ImmutableArray.Empty; } EditSession session = encService.EditSession; @@ -42,20 +42,18 @@ public override async Task AnalyzeSemanticsAsync(Document document, Action.Empty; } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var analysis = await session.GetDocumentAnalysis(document).GetValueAsync(cancellationToken).ConfigureAwait(false); - if (!analysis.RudeEditErrors.IsDefault) + if (analysis.RudeEditErrors.IsDefault) { - session.LogRudeEditErrors(analysis.RudeEditErrors); - - foreach (var error in analysis.RudeEditErrors) - { - addDiagnostic(error.ToDiagnostic(tree)); - } + return ImmutableArray.Empty; } + + session.LogRudeEditErrors(analysis.RudeEditErrors); + return analysis.RudeEditErrors.SelectAsArray((e, t) => e.ToDiagnostic(t), tree); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { diff --git a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs index 66ad5d2eed075..96e3dba336951 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs @@ -171,21 +171,17 @@ public async Task> GetSyntaxDiagnosticsAsync(Diagnost var documentAnalyzer = analyzer as DocumentDiagnosticAnalyzer; if (documentAnalyzer != null) { - using (var pooledObject = SharedPools.Default>().GetPooledObject()) + _cancellationToken.ThrowIfCancellationRequested(); + + try + { + var diagnostics = await documentAnalyzer.AnalyzeSyntaxAsync(_document, _cancellationToken).ConfigureAwait(false); + return GetFilteredDocumentDiagnostics(diagnostics, compilation); + } + catch (Exception e) when (!IsCanceled(e, _cancellationToken)) { - var diagnostics = pooledObject.Object; - _cancellationToken.ThrowIfCancellationRequested(); - - try - { - await documentAnalyzer.AnalyzeSyntaxAsync(_document, diagnostics.Add, _cancellationToken).ConfigureAwait(false); - return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray(); - } - catch (Exception e) when (!IsCanceled(e, _cancellationToken)) - { - OnAnalyzerException(e, analyzer, compilation); - return ImmutableArray.Empty; - } + OnAnalyzerException(e, analyzer, compilation); + return ImmutableArray.Empty; } } @@ -197,7 +193,7 @@ public async Task> GetSyntaxDiagnosticsAsync(Diagnost var compilationWithAnalyzers = GetCompilationWithAnalyzers(compilation); var syntaxDiagnostics = await compilationWithAnalyzers.GetAnalyzerSyntaxDiagnosticsAsync(_root.SyntaxTree, ImmutableArray.Create(analyzer), _cancellationToken).ConfigureAwait(false); await UpdateAnalyzerTelemetryDataAsync(analyzer, compilationWithAnalyzers).ConfigureAwait(false); - return GetFilteredDocumentDiagnostics(syntaxDiagnostics, compilation, onlyLocationFiltering: true).ToImmutableArray(); + return syntaxDiagnostics.WhereAsArray(IsLocalDiagnostic); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { @@ -205,25 +201,25 @@ public async Task> GetSyntaxDiagnosticsAsync(Diagnost } } - private IEnumerable GetFilteredDocumentDiagnostics(IEnumerable diagnostics, Compilation compilation, bool onlyLocationFiltering = false) + private ImmutableArray GetFilteredDocumentDiagnostics(ImmutableArray diagnostics, Compilation compilationOpt) { if (_root == null) { return diagnostics; } - return GetFilteredDocumentDiagnosticsCore(diagnostics, compilation, onlyLocationFiltering); + if (compilationOpt == null) + { + return diagnostics.WhereAsArray(IsLocalDiagnostic); + } + + return CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics.Where(IsLocalDiagnostic), compilationOpt).ToImmutableArray(); } - private IEnumerable GetFilteredDocumentDiagnosticsCore(IEnumerable diagnostics, Compilation compilation, bool onlyLocationFiltering) + private bool IsLocalDiagnostic(Diagnostic diagnostic) { - var diagsFilteredByLocation = diagnostics.Where(diagnostic => (diagnostic.Location == Location.None) || - (diagnostic.Location.SourceTree == _root.SyntaxTree && - (_span == null || diagnostic.Location.SourceSpan.IntersectsWith(_span.Value)))); - - return compilation == null || onlyLocationFiltering - ? diagsFilteredByLocation - : CompilationWithAnalyzers.GetEffectiveDiagnostics(diagsFilteredByLocation, compilation); + return diagnostic.Location == Location.None || + diagnostic.Location.SourceTree == _root.SyntaxTree && (_span == null || diagnostic.Location.SourceSpan.IntersectsWith(_span.Value)); } internal void OnAnalyzerException(Exception ex, DiagnosticAnalyzer analyzer, Compilation compilation) @@ -251,23 +247,20 @@ public async Task> GetSemanticDiagnosticsAsync(Diagno var documentAnalyzer = analyzer as DocumentDiagnosticAnalyzer; if (documentAnalyzer != null) { - using (var pooledObject = SharedPools.Default>().GetPooledObject()) + _cancellationToken.ThrowIfCancellationRequested(); + + ImmutableArray diagnostics; + try { - var diagnostics = pooledObject.Object; - _cancellationToken.ThrowIfCancellationRequested(); - - try - { - await documentAnalyzer.AnalyzeSemanticsAsync(_document, diagnostics.Add, _cancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (!IsCanceled(e, _cancellationToken)) - { - OnAnalyzerException(e, analyzer, compilation); - return ImmutableArray.Empty; - } - - return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray(); + diagnostics = await documentAnalyzer.AnalyzeSemanticsAsync(_document, _cancellationToken).ConfigureAwait(false); } + catch (Exception e) when (!IsCanceled(e, _cancellationToken)) + { + OnAnalyzerException(e, analyzer, compilation); + return ImmutableArray.Empty; + } + + return GetFilteredDocumentDiagnostics(diagnostics, compilation); } if (!_document.SupportsSyntaxTree || compilation == null) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index d97a6125b81cb..ad30f1de894d1 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -323,30 +323,38 @@ private async Task> ComputeProjectDiagnosticAnalyzerDiag private async Task> ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync( Document document, DocumentDiagnosticAnalyzer analyzer, AnalysisKind kind, Compilation compilationOpt, CancellationToken cancellationToken) { - using (var pooledObject = SharedPools.Default>().GetPooledObject()) + cancellationToken.ThrowIfCancellationRequested(); + + try { - var diagnostics = pooledObject.Object; - cancellationToken.ThrowIfCancellationRequested(); + Task> analyzeAsync; - try + switch (kind) { - switch (kind) - { - case AnalysisKind.Syntax: - await analyzer.AnalyzeSyntaxAsync(document, diagnostics.Add, cancellationToken).ConfigureAwait(false); - return compilationOpt == null ? diagnostics.ToImmutableArrayOrEmpty() : CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt); - case AnalysisKind.Semantic: - await analyzer.AnalyzeSemanticsAsync(document, diagnostics.Add, cancellationToken).ConfigureAwait(false); - return compilationOpt == null ? diagnostics.ToImmutableArrayOrEmpty() : CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt); - default: - return Contract.FailWithReturn>("shouldn't reach here"); - } + case AnalysisKind.Syntax: + analyzeAsync = analyzer.AnalyzeSyntaxAsync(document, cancellationToken); + break; + + case AnalysisKind.Semantic: + analyzeAsync = analyzer.AnalyzeSemanticsAsync(document, cancellationToken); + break; + + default: + throw ExceptionUtilities.UnexpectedValue(kind); } - catch (Exception e) when (!IsCanceled(e, cancellationToken)) + + var diagnostics = (await analyzeAsync.ConfigureAwait(false)).NullToEmpty(); + if (compilationOpt != null) { - OnAnalyzerException(analyzer, document.Project.Id, compilationOpt, e); - return ImmutableArray.Empty; + return CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationOpt); } + + return diagnostics; + } + catch (Exception e) when (!IsCanceled(e, cancellationToken)) + { + OnAnalyzerException(analyzer, document.Project.Id, compilationOpt, e); + return ImmutableArray.Empty; } } diff --git a/src/VisualStudio/Core/Def/Implementation/EditAndContinue/VsENCRebuildableProjectImpl.cs b/src/VisualStudio/Core/Def/Implementation/EditAndContinue/VsENCRebuildableProjectImpl.cs index d4694fc61e648..64b40e516582b 100644 --- a/src/VisualStudio/Core/Def/Implementation/EditAndContinue/VsENCRebuildableProjectImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/EditAndContinue/VsENCRebuildableProjectImpl.cs @@ -79,7 +79,7 @@ internal sealed class VsENCRebuildableProjectImpl private EmitBaseline _pendingBaseline; private Project _projectBeingEmitted; - private ImmutableArray _documentsWithEmitError; + private ImmutableArray _documentsWithEmitError = ImmutableArray.Empty; /// /// Initialized when the project switches to debug state. @@ -251,11 +251,11 @@ public int StartDebuggingPE() var descriptor = new DiagnosticDescriptor("Metadata", "Metadata", ServicesVSResources.ErrorWhileReading, DiagnosticCategory.EditAndContinue, DiagnosticSeverity.Error, isEnabledByDefault: true, customTags: DiagnosticCustomTags.EditAndContinue); - _diagnosticProvider.ReportDiagnostics(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.DebuggerErrorId, _vsProject.Id, _encService.DebuggingSession.InitialSolution, - new[] - { - Diagnostic.Create(descriptor, Location.None, outputPath, e.Message) - }); + _diagnosticProvider.ReportDiagnostics( + new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.DebuggerErrorId), + _encService.DebuggingSession.InitialSolution, + _vsProject.Id, + new[] { Diagnostic.Create(descriptor, Location.None, outputPath, e.Message) }); } } else @@ -322,7 +322,8 @@ public int StopDebuggingPE() else { // an error might have been reported: - _diagnosticProvider.ClearDiagnostics(_encService.DebuggingSession, _vsProject.Workspace, EditAndContinueDiagnosticUpdateSource.DebuggerErrorId, _vsProject.Id, documentId: null); + var errorId = new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.DebuggerErrorId); + _diagnosticProvider.ClearDiagnostics(errorId, _vsProject.Workspace.CurrentSolution, _vsProject.Id, documentIdOpt: null); } _activeMethods = null; @@ -908,8 +909,14 @@ public int ExitBreakStateOnPE() Debug.Assert(s_breakStateProjectCount >= 0); _changesApplied = false; - _diagnosticProvider.ClearDiagnostics(_encService.DebuggingSession, _vsProject.Workspace, EditAndContinueDiagnosticUpdateSource.EmitErrorId, _vsProject.Id, _documentsWithEmitError); - _documentsWithEmitError = default(ImmutableArray); + + _diagnosticProvider.ClearDiagnostics( + new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.EmitErrorId), + _vsProject.Workspace.CurrentSolution, + _vsProject.Id, + _documentsWithEmitError); + + _documentsWithEmitError = ImmutableArray.Empty; } // HResult ignored by the debugger @@ -971,25 +978,21 @@ public unsafe int BuildForEnc(object pUpdatePE) delta = emitTask.Result; } + var errorId = new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.EmitErrorId); + // Clear diagnostics, in case the project was built before and failed due to errors. - _diagnosticProvider.ClearDiagnostics(_encService.DebuggingSession, _vsProject.Workspace, EditAndContinueDiagnosticUpdateSource.EmitErrorId, _vsProject.Id, _documentsWithEmitError); + _diagnosticProvider.ClearDiagnostics(errorId, _projectBeingEmitted.Solution, _vsProject.Id, _documentsWithEmitError); if (!delta.EmitResult.Success) { var errors = delta.EmitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error); - _documentsWithEmitError = _diagnosticProvider.ReportDiagnostics( - _encService.DebuggingSession, - EditAndContinueDiagnosticUpdateSource.EmitErrorId, - _vsProject.Id, - _projectBeingEmitted.Solution, - errors); - + _documentsWithEmitError = _diagnosticProvider.ReportDiagnostics(errorId, _projectBeingEmitted.Solution, _vsProject.Id, errors); _encService.EditSession.LogEmitProjectDeltaErrors(errors.Select(e => e.Id)); return VSConstants.E_FAIL; } - _documentsWithEmitError = default(ImmutableArray); + _documentsWithEmitError = ImmutableArray.Empty; SetFileUpdates(updater, delta.LineEdits); updater.SetDeltaIL(delta.IL.Value, (uint)delta.IL.Value.Length);