From f18d016a1109e9432ab10e4e03e0d0f9576b237d Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 22 Oct 2019 16:32:06 -0700 Subject: [PATCH 1/7] Enable logging of informational telemetry for analyzers/fixers/refactorings executed during Ctrl + . If user enables informational RoslynEventSource telemetry following the steps at https://aka.ms/reportPerf, we log the execution times for each analyzer/fixer/refactorings when computing the code fixes and refactorings to show in the light bulb. This will help us identify slow analyzers/fixers/refactorings that lead to UI delays with "Gathering suggestions" dialog. --- .../CodeFixes/CodeFixService.cs | 42 ++++++- .../CodeRefactoringService.cs | 22 +++- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 65 +++++++---- src/Workspaces/Core/Portable/Log/EtwLogger.cs | 6 +- .../Core/Portable/Log/FunctionId.cs | 3 + .../Log/RoslynEventSource.LogBlock.cs | 105 ++++++++++++++++++ .../Core/Portable/Log/RoslynEventSource.cs | 2 +- ...ryService.AbstractRecoverableSyntaxRoot.cs | 45 +------- 8 files changed, 214 insertions(+), 76 deletions(-) create mode 100644 src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs index 08d654d91d139..33d488e2ef669 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics; +using System.Diagnostics.Tracing; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -14,6 +15,7 @@ using Microsoft.CodeAnalysis.ErrorLogger; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -292,12 +294,31 @@ private async Task AppendFixesAsync( } var extensionManager = document.Project.Solution.Workspace.Services.GetService(); + var isPerProviderLoggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); // run each CodeFixProvider to gather individual CodeFixes for reported diagnostics foreach (var fixer in allFixers.Distinct()) { cancellationToken.ThrowIfCancellationRequested(); + if (isPerProviderLoggingEnabled) + { + using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixer.ToString(), cancellationToken)) + { + await ProcessFixerAsync(fixer).ConfigureAwait(false); + } + } + else + { + await ProcessFixerAsync(fixer).ConfigureAwait(false); + } + + // Just need the first result if we are doing fix all in span + if (fixAllForInSpan && result.Any()) return; + } + + async Task ProcessFixerAsync(CodeFixProvider fixer) + { await AppendFixesOrConfigurationsAsync( document, span, diagnostics, fixAllForInSpan, result, fixer, hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), @@ -307,7 +328,6 @@ await AppendFixesOrConfigurationsAsync( { var primaryDiagnostic = dxs.First(); return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, isBlocking, ImmutableArray.Create(primaryDiagnostic), cancellationToken); - } else { @@ -315,9 +335,6 @@ await AppendFixesOrConfigurationsAsync( } }, cancellationToken: cancellationToken).ConfigureAwait(false); - - // Just need the first result if we are doing fix all in span - if (fixAllForInSpan && result.Any()) return; } } @@ -354,8 +371,25 @@ private async Task AppendConfigurationsAsync( return; } + var isPerProviderLoggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); + // append CodeFixCollection for each CodeFixProvider foreach (var provider in lazyConfigurationProviders.Value) + { + if (isPerProviderLoggingEnabled) + { + using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, provider.ToString(), cancellationToken)) + { + await AppendConfigurationsAsync(provider).ConfigureAwait(false); + } + } + else + { + await AppendConfigurationsAsync(provider).ConfigureAwait(false); + } + } + + async Task AppendConfigurationsAsync(IConfigurationFixProvider provider) { await AppendFixesOrConfigurationsAsync( document, diagnosticsSpan, diagnostics, fixAllForInSpan: false, result, provider, diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index bd1aaeaa8a7e4..91f144c8771e2 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics.Tracing; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -103,20 +104,37 @@ async Task> ICodeRefactoringService.GetRefactori bool isBlocking, CancellationToken cancellationToken) { + var extensionManager = document.Project.Solution.Workspace.Services.GetService(); + var isPerProviderLoggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); + using (Logger.LogBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, cancellationToken)) { - var extensionManager = document.Project.Solution.Workspace.Services.GetService(); var tasks = new List>(); foreach (var provider in GetProviders(document)) { tasks.Add(Task.Run( - () => GetRefactoringFromProviderAsync(document, state, provider, extensionManager, isBlocking, cancellationToken), cancellationToken)); + () => GetRefactoringsAsync(provider), cancellationToken)); } var results = await Task.WhenAll(tasks).ConfigureAwait(false); return results.WhereNotNull().ToImmutableArray(); } + + Task GetRefactoringsAsync(CodeRefactoringProvider provider) + { + if (isPerProviderLoggingEnabled) + { + using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, provider.ToString(), cancellationToken)) + { + return GetRefactoringFromProviderAsync(document, state, provider, extensionManager, isBlocking, cancellationToken); + } + } + else + { + return GetRefactoringFromProviderAsync(document, state, provider, extensionManager, isBlocking, cancellationToken); + } + } } private async Task GetRefactoringFromProviderAsync( diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 86d8c39f9dc2c..474852870e23f 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -4,10 +4,12 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.Tracing; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -106,37 +108,24 @@ private LatestDiagnosticsForSpanGetter( public async Task TryGetAsync(List list, CancellationToken cancellationToken) { + var containsFullResult = true; + var loggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); + try { - var containsFullResult = true; foreach (var stateSet in _stateSets) { - cancellationToken.ThrowIfCancellationRequested(); - - containsFullResult &= await TryGetSyntaxAndSemanticDiagnosticsAsync(stateSet, list, cancellationToken).ConfigureAwait(false); - - // check whether compilation end code fix is enabled - if (!_document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.CompilationEndCodeFix)) - { - continue; - } - - // check whether heuristic is enabled - if (_blockForData && _document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.UseCompilationEndCodeFixHeuristic)) + if (loggingEnabled) { - var avoidLoadingData = true; - var state = stateSet.GetProjectState(_project.Id); - var result = await state.GetAnalysisDataAsync(_document, avoidLoadingData, cancellationToken).ConfigureAwait(false); - - // no previous compilation end diagnostics in this file. - var version = await GetDiagnosticVersionAsync(_project, cancellationToken).ConfigureAwait(false); - if (state.IsEmpty(_document.Id) || result.Version != version) + using (RoslynEventSource.LogInformationalBlock(FunctionId.DiagnosticAnalyzerService_GetDiagnosticsForSpanAsync, stateSet.Analyzer.GetAnalyzerId(), cancellationToken)) { - continue; + await ProcessStateSetAsync(stateSet).ConfigureAwait(false); } } - - containsFullResult &= await TryGetProjectDiagnosticsAsync(stateSet, GetProjectDiagnosticsAsync, list, cancellationToken).ConfigureAwait(false); + else + { + await ProcessStateSetAsync(stateSet).ConfigureAwait(false); + } } // if we are blocked for data, then we should always have full result. @@ -147,6 +136,36 @@ public async Task TryGetAsync(List list, CancellationToken { throw ExceptionUtilities.Unreachable; } + + async Task ProcessStateSetAsync(StateSet stateSet) + { + cancellationToken.ThrowIfCancellationRequested(); + + containsFullResult &= await TryGetSyntaxAndSemanticDiagnosticsAsync(stateSet, list, cancellationToken).ConfigureAwait(false); + + // check whether compilation end code fix is enabled + if (!_document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.CompilationEndCodeFix)) + { + return; + } + + // check whether heuristic is enabled + if (_blockForData && _document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.UseCompilationEndCodeFixHeuristic)) + { + var avoidLoadingData = true; + var state = stateSet.GetProjectState(_project.Id); + var result = await state.GetAnalysisDataAsync(_document, avoidLoadingData, cancellationToken).ConfigureAwait(false); + + // no previous compilation end diagnostics in this file. + var version = await GetDiagnosticVersionAsync(_project, cancellationToken).ConfigureAwait(false); + if (state.IsEmpty(_document.Id) || result.Version != version) + { + return; + } + } + + containsFullResult &= await TryGetProjectDiagnosticsAsync(stateSet, GetProjectDiagnosticsAsync, list, cancellationToken).ConfigureAwait(false); + } } private async Task TryGetSyntaxAndSemanticDiagnosticsAsync(StateSet stateSet, List list, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Log/EtwLogger.cs b/src/Workspaces/Core/Portable/Log/EtwLogger.cs index a84a9a69b6ad8..c8dd71e0f6ebb 100644 --- a/src/Workspaces/Core/Portable/Log/EtwLogger.cs +++ b/src/Workspaces/Core/Portable/Log/EtwLogger.cs @@ -40,18 +40,18 @@ public void Log(FunctionId functionId, LogMessage logMessage) public void LogBlockStart(FunctionId functionId, LogMessage logMessage, int uniquePairId, CancellationToken cancellationToken) { - RoslynEventSource.Instance.BlockStart(GetMessage(logMessage), functionId, uniquePairId); + _source.BlockStart(GetMessage(logMessage), functionId, uniquePairId); } public void LogBlockEnd(FunctionId functionId, LogMessage logMessage, int uniquePairId, int delta, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { - RoslynEventSource.Instance.BlockCanceled(functionId, delta, uniquePairId); + _source.BlockCanceled(functionId, delta, uniquePairId); } else { - RoslynEventSource.Instance.BlockStop(functionId, delta, uniquePairId); + _source.BlockStop(functionId, delta, uniquePairId); } } diff --git a/src/Workspaces/Core/Portable/Log/FunctionId.cs b/src/Workspaces/Core/Portable/Log/FunctionId.cs index 8d0ae6c5d79ce..932180d967384 100644 --- a/src/Workspaces/Core/Portable/Log/FunctionId.cs +++ b/src/Workspaces/Core/Portable/Log/FunctionId.cs @@ -466,5 +466,8 @@ internal enum FunctionId Liveshare_SyntacticTagger, CommandHandler_GoToBase, + + DiagnosticAnalyzerService_GetDiagnosticsForSpanAsync, + CodeFixes_GetCodeFixesAsync, } } diff --git a/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs b/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs new file mode 100644 index 0000000000000..9c5ae461884fb --- /dev/null +++ b/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs @@ -0,0 +1,105 @@ +// 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.Diagnostics.Tracing; +using System.Threading; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.Internal.Log +{ + internal partial class RoslynEventSource + { + // Regardless of how many tasks we can run in parallel on the machine, we likely won't need more than 256 + // instrumentation points in flight at a given time. + // Use an object pool since we may be logging up to 1-10k events/second + private static readonly ObjectPool s_pool = new ObjectPool(() => new RoslynLogBlock(s_pool), Math.Min(Environment.ProcessorCount * 8, 256)); + + /// + /// next unique block id that will be given to each LogBlock + /// + private static int s_lastUniqueBlockId; + + /// + /// Logs a block with the given and specified . + /// On dispose of the returned disposable object, it logs the 'tick' count between the start and end of the block. + /// Unlike other logging methods on , this method does not check + /// if the specified was explicitly enabled. + /// Instead it checks if the was enabled at level. + /// + public static IDisposable LogInformationalBlock(FunctionId functionId, string message, CancellationToken cancellationToken) + => LogBlock(functionId, message, EventLevel.Informational, cancellationToken); + + private static IDisposable LogBlock(FunctionId functionId, string message, EventLevel requiredEventLevel, CancellationToken cancellationToken) + { + if (!Instance.IsEnabled(requiredEventLevel, EventKeywords.None)) + { + return EmptyLogBlock.Instance; + } + + return CreateLogBlock(functionId, message, cancellationToken); + } + + /// + /// return next unique pair id + /// + private static int GetNextUniqueBlockId() + { + return Interlocked.Increment(ref s_lastUniqueBlockId); + } + + private static IDisposable CreateLogBlock(FunctionId functionId, string message, CancellationToken cancellationToken) + { + var block = s_pool.Allocate(); + var blockId = GetNextUniqueBlockId(); + block.Construct(functionId, message, blockId, cancellationToken); + return block; + } + + /// + /// This tracks the logged message. On instantiation, it logs 'Started block' with other event data. + /// On dispose, it logs 'Ended block' with the same event data so we can track which block started and ended when looking at logs. + /// + private class RoslynLogBlock : IDisposable + { + private readonly ObjectPool _pool; + private CancellationToken _cancellationToken; + + private FunctionId _functionId; + private int _tick; + private int _blockId; + + public RoslynLogBlock(ObjectPool pool) + { + _pool = pool; + } + + public void Construct(FunctionId functionId, string message, int blockId, CancellationToken cancellationToken) + { + _functionId = functionId; + _tick = Environment.TickCount; + _blockId = blockId; + _cancellationToken = cancellationToken; + + Instance.BlockStart(message, functionId, blockId); + } + + public void Dispose() + { + // This delta is valid for durations of < 25 days + var delta = Environment.TickCount - _tick; + + if (_cancellationToken.IsCancellationRequested) + { + Instance.BlockCanceled(_functionId, delta, _blockId); + } + else + { + Instance.BlockStop(_functionId, delta, _blockId); + } + + // Free this block back to the pool + _pool.Free(this); + } + } + } +} diff --git a/src/Workspaces/Core/Portable/Log/RoslynEventSource.cs b/src/Workspaces/Core/Portable/Log/RoslynEventSource.cs index 36b2d44350d72..856156919ca81 100644 --- a/src/Workspaces/Core/Portable/Log/RoslynEventSource.cs +++ b/src/Workspaces/Core/Portable/Log/RoslynEventSource.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.Internal.Log /// "\\clrmain\tools\managed\etw\eventRegister\bin\Debug\eventRegister.exe" Microsoft.CodeAnalysis.Workspaces.dll /// [EventSource(Name = "RoslynEventSource")] - internal sealed class RoslynEventSource : EventSource + internal sealed partial class RoslynEventSource : EventSource { // might not "enabled" but we always have this singleton alive public static readonly RoslynEventSource Instance = new RoslynEventSource(); diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs index 3eed20e5913dc..91fc2fb2b209d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs @@ -160,32 +160,11 @@ protected override async Task RecoverAsync(CancellationToken cancellation { Contract.ThrowIfNull(_storage); - var tickCount = Environment.TickCount; - try + using (RoslynEventSource.LogInformationalBlock(FunctionId.Workspace_Recoverable_RecoverRootAsync, _containingTree.FilePath, cancellationToken)) { - if (RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None)) - { - RoslynEventSource.Instance.BlockStart(_containingTree.FilePath, FunctionId.Workspace_Recoverable_RecoverRootAsync, blockId: 0); - } - using var stream = await _storage.ReadStreamAsync(cancellationToken).ConfigureAwait(false); return RecoverRoot(stream, cancellationToken); } - finally - { - if (RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None)) - { - var tick = Environment.TickCount - tickCount; - if (cancellationToken.IsCancellationRequested) - { - RoslynEventSource.Instance.BlockCanceled(FunctionId.Workspace_Recoverable_RecoverRootAsync, tick, blockId: 0); - } - else - { - RoslynEventSource.Instance.BlockStop(FunctionId.Workspace_Recoverable_RecoverRootAsync, tick, blockId: 0); - } - } - } } protected override TRoot Recover(CancellationToken cancellationToken) @@ -193,31 +172,11 @@ protected override TRoot Recover(CancellationToken cancellationToken) Contract.ThrowIfNull(_storage); var tickCount = Environment.TickCount; - try + using (RoslynEventSource.LogInformationalBlock(FunctionId.Workspace_Recoverable_RecoverRoot, _containingTree.FilePath, cancellationToken)) { - if (RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None)) - { - RoslynEventSource.Instance.BlockStart(_containingTree.FilePath, FunctionId.Workspace_Recoverable_RecoverRoot, blockId: 0); - } - using var stream = _storage.ReadStream(cancellationToken); return RecoverRoot(stream, cancellationToken); } - finally - { - if (RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None)) - { - var tick = Environment.TickCount - tickCount; - if (cancellationToken.IsCancellationRequested) - { - RoslynEventSource.Instance.BlockCanceled(FunctionId.Workspace_Recoverable_RecoverRoot, tick, blockId: 0); - } - else - { - RoslynEventSource.Instance.BlockStop(FunctionId.Workspace_Recoverable_RecoverRoot, tick, blockId: 0); - } - } - } } private TRoot RecoverRoot(Stream stream, CancellationToken cancellationToken) From 92d723ff01a2aac84e7bc68cf4f351e5585c8eea Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 23 Oct 2019 10:29:22 -0700 Subject: [PATCH 2/7] Address review feedback --- .../CodeFixes/CodeFixService.cs | 75 +++----- .../CodeRefactoringService.cs | 24 +-- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 66 +++---- .../Log/RoslynEventSource.LogBlock.cs | 161 ++++++++++++------ 4 files changed, 167 insertions(+), 159 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs index 33d488e2ef669..5fb68e54727e4 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs @@ -294,48 +294,35 @@ private async Task AppendFixesAsync( } var extensionManager = document.Project.Solution.Workspace.Services.GetService(); - var isPerProviderLoggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); // run each CodeFixProvider to gather individual CodeFixes for reported diagnostics foreach (var fixer in allFixers.Distinct()) { cancellationToken.ThrowIfCancellationRequested(); - if (isPerProviderLoggingEnabled) + using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixer, cancellationToken)) { - using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixer.ToString(), cancellationToken)) - { - await ProcessFixerAsync(fixer).ConfigureAwait(false); - } - } - else - { - await ProcessFixerAsync(fixer).ConfigureAwait(false); + await AppendFixesOrConfigurationsAsync( + document, span, diagnostics, fixAllForInSpan, result, fixer, + hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), + getFixes: dxs => + { + if (fixAllForInSpan) + { + var primaryDiagnostic = dxs.First(); + return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, isBlocking, ImmutableArray.Create(primaryDiagnostic), cancellationToken); + } + else + { + return GetCodeFixesAsync(document, span, fixer, isBlocking, dxs, cancellationToken); + } + }, + cancellationToken: cancellationToken).ConfigureAwait(false); } // Just need the first result if we are doing fix all in span if (fixAllForInSpan && result.Any()) return; } - - async Task ProcessFixerAsync(CodeFixProvider fixer) - { - await AppendFixesOrConfigurationsAsync( - document, span, diagnostics, fixAllForInSpan, result, fixer, - hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), - getFixes: dxs => - { - if (fixAllForInSpan) - { - var primaryDiagnostic = dxs.First(); - return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, isBlocking, ImmutableArray.Create(primaryDiagnostic), cancellationToken); - } - else - { - return GetCodeFixesAsync(document, span, fixer, isBlocking, dxs, cancellationToken); - } - }, - cancellationToken: cancellationToken).ConfigureAwait(false); - } } private async Task> GetCodeFixesAsync( @@ -371,33 +358,19 @@ private async Task AppendConfigurationsAsync( return; } - var isPerProviderLoggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); - // append CodeFixCollection for each CodeFixProvider foreach (var provider in lazyConfigurationProviders.Value) { - if (isPerProviderLoggingEnabled) - { - using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, provider.ToString(), cancellationToken)) - { - await AppendConfigurationsAsync(provider).ConfigureAwait(false); - } - } - else + using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, provider, cancellationToken)) { - await AppendConfigurationsAsync(provider).ConfigureAwait(false); + await AppendFixesOrConfigurationsAsync( + document, diagnosticsSpan, diagnostics, fixAllForInSpan: false, result, provider, + hasFix: d => provider.IsFixableDiagnostic(d), + getFixes: dxs => provider.GetFixesAsync( + document, diagnosticsSpan, dxs, cancellationToken), + cancellationToken: cancellationToken).ConfigureAwait(false); } } - - async Task AppendConfigurationsAsync(IConfigurationFixProvider provider) - { - await AppendFixesOrConfigurationsAsync( - document, diagnosticsSpan, diagnostics, fixAllForInSpan: false, result, provider, - hasFix: d => provider.IsFixableDiagnostic(d), - getFixes: dxs => provider.GetFixesAsync( - document, diagnosticsSpan, dxs, cancellationToken), - cancellationToken: cancellationToken).ConfigureAwait(false); - } } private async Task AppendFixesOrConfigurationsAsync( diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index 91f144c8771e2..1e7636b5a5d62 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -114,27 +114,19 @@ async Task> ICodeRefactoringService.GetRefactori foreach (var provider in GetProviders(document)) { tasks.Add(Task.Run( - () => GetRefactoringsAsync(provider), cancellationToken)); + () => + { + using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, provider, cancellationToken)) + { + return GetRefactoringFromProviderAsync(document, state, provider, extensionManager, isBlocking, cancellationToken); + } + }, + cancellationToken)); } var results = await Task.WhenAll(tasks).ConfigureAwait(false); return results.WhereNotNull().ToImmutableArray(); } - - Task GetRefactoringsAsync(CodeRefactoringProvider provider) - { - if (isPerProviderLoggingEnabled) - { - using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, provider.ToString(), cancellationToken)) - { - return GetRefactoringFromProviderAsync(document, state, provider, extensionManager, isBlocking, cancellationToken); - } - } - else - { - return GetRefactoringFromProviderAsync(document, state, provider, extensionManager, isBlocking, cancellationToken); - } - } } private async Task GetRefactoringFromProviderAsync( diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 474852870e23f..bd2643208dfca 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -108,23 +108,39 @@ private LatestDiagnosticsForSpanGetter( public async Task TryGetAsync(List list, CancellationToken cancellationToken) { - var containsFullResult = true; - var loggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); - try { + var containsFullResult = true; foreach (var stateSet in _stateSets) { - if (loggingEnabled) + using (RoslynEventSource.LogInformationalBlock(FunctionId.DiagnosticAnalyzerService_GetDiagnosticsForSpanAsync, stateSet.Analyzer, cancellationToken)) { - using (RoslynEventSource.LogInformationalBlock(FunctionId.DiagnosticAnalyzerService_GetDiagnosticsForSpanAsync, stateSet.Analyzer.GetAnalyzerId(), cancellationToken)) + cancellationToken.ThrowIfCancellationRequested(); + + containsFullResult &= await TryGetSyntaxAndSemanticDiagnosticsAsync(stateSet, list, cancellationToken).ConfigureAwait(false); + + // check whether compilation end code fix is enabled + if (!_document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.CompilationEndCodeFix)) { - await ProcessStateSetAsync(stateSet).ConfigureAwait(false); + continue; } - } - else - { - await ProcessStateSetAsync(stateSet).ConfigureAwait(false); + + // check whether heuristic is enabled + if (_blockForData && _document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.UseCompilationEndCodeFixHeuristic)) + { + var avoidLoadingData = true; + var state = stateSet.GetProjectState(_project.Id); + var result = await state.GetAnalysisDataAsync(_document, avoidLoadingData, cancellationToken).ConfigureAwait(false); + + // no previous compilation end diagnostics in this file. + var version = await GetDiagnosticVersionAsync(_project, cancellationToken).ConfigureAwait(false); + if (state.IsEmpty(_document.Id) || result.Version != version) + { + continue; + } + } + + containsFullResult &= await TryGetProjectDiagnosticsAsync(stateSet, GetProjectDiagnosticsAsync, list, cancellationToken).ConfigureAwait(false); } } @@ -136,36 +152,6 @@ public async Task TryGetAsync(List list, CancellationToken { throw ExceptionUtilities.Unreachable; } - - async Task ProcessStateSetAsync(StateSet stateSet) - { - cancellationToken.ThrowIfCancellationRequested(); - - containsFullResult &= await TryGetSyntaxAndSemanticDiagnosticsAsync(stateSet, list, cancellationToken).ConfigureAwait(false); - - // check whether compilation end code fix is enabled - if (!_document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.CompilationEndCodeFix)) - { - return; - } - - // check whether heuristic is enabled - if (_blockForData && _document.Project.Solution.Workspace.Options.GetOption(InternalDiagnosticsOptions.UseCompilationEndCodeFixHeuristic)) - { - var avoidLoadingData = true; - var state = stateSet.GetProjectState(_project.Id); - var result = await state.GetAnalysisDataAsync(_document, avoidLoadingData, cancellationToken).ConfigureAwait(false); - - // no previous compilation end diagnostics in this file. - var version = await GetDiagnosticVersionAsync(_project, cancellationToken).ConfigureAwait(false); - if (state.IsEmpty(_document.Id) || result.Version != version) - { - return; - } - } - - containsFullResult &= await TryGetProjectDiagnosticsAsync(stateSet, GetProjectDiagnosticsAsync, list, cancellationToken).ConfigureAwait(false); - } } private async Task TryGetSyntaxAndSemanticDiagnosticsAsync(StateSet stateSet, List list, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs b/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs index 9c5ae461884fb..d2042f6311ff8 100644 --- a/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs +++ b/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs @@ -1,90 +1,150 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +#nullable enable + using System; +using System.Diagnostics; using System.Diagnostics.Tracing; using System.Threading; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Internal.Log { internal partial class RoslynEventSource { - // Regardless of how many tasks we can run in parallel on the machine, we likely won't need more than 256 - // instrumentation points in flight at a given time. - // Use an object pool since we may be logging up to 1-10k events/second - private static readonly ObjectPool s_pool = new ObjectPool(() => new RoslynLogBlock(s_pool), Math.Min(Environment.ProcessorCount * 8, 256)); - /// - /// next unique block id that will be given to each LogBlock + /// Logs an informational block with given 's representation as the message + /// and specified . + /// On dispose of the returned disposable object, it logs the 'tick' count between the start and end of the block. + /// Unlike other logging methods on , this method does not check + /// if the specified was explicitly enabled. + /// Instead it checks if the was enabled at level. /// - private static int s_lastUniqueBlockId; + public static IDisposable LogInformationalBlock(FunctionId functionId, object entity, CancellationToken cancellationToken) + => LogBlock.Create(functionId, entity, EventLevel.Informational, cancellationToken); /// - /// Logs a block with the given and specified . + /// Logs an informational message block with the given > and specified . /// On dispose of the returned disposable object, it logs the 'tick' count between the start and end of the block. /// Unlike other logging methods on , this method does not check /// if the specified was explicitly enabled. /// Instead it checks if the was enabled at level. /// public static IDisposable LogInformationalBlock(FunctionId functionId, string message, CancellationToken cancellationToken) - => LogBlock(functionId, message, EventLevel.Informational, cancellationToken); - - private static IDisposable LogBlock(FunctionId functionId, string message, EventLevel requiredEventLevel, CancellationToken cancellationToken) - { - if (!Instance.IsEnabled(requiredEventLevel, EventKeywords.None)) - { - return EmptyLogBlock.Instance; - } - - return CreateLogBlock(functionId, message, cancellationToken); - } - - /// - /// return next unique pair id - /// - private static int GetNextUniqueBlockId() - { - return Interlocked.Increment(ref s_lastUniqueBlockId); - } - - private static IDisposable CreateLogBlock(FunctionId functionId, string message, CancellationToken cancellationToken) - { - var block = s_pool.Allocate(); - var blockId = GetNextUniqueBlockId(); - block.Construct(functionId, message, blockId, cancellationToken); - return block; - } + => LogBlock.Create(functionId, message, EventLevel.Informational, cancellationToken); /// /// This tracks the logged message. On instantiation, it logs 'Started block' with other event data. /// On dispose, it logs 'Ended block' with the same event data so we can track which block started and ended when looking at logs. /// - private class RoslynLogBlock : IDisposable + private struct LogBlock : IDisposable { - private readonly ObjectPool _pool; - private CancellationToken _cancellationToken; + private readonly FunctionId _functionId; + private readonly object? _entityForMessage; + private readonly EventLevel _eventLevel; + private readonly int _blockId; + private readonly CancellationToken _cancellationToken; - private FunctionId _functionId; private int _tick; - private int _blockId; - - public RoslynLogBlock(ObjectPool pool) + private bool _startLogged; + private string? _message; + + /// + /// next unique block id that will be given to each LogBlock + /// + private static int s_lastUniqueBlockId; + + private LogBlock( + FunctionId functionId, + string? message, + object? entityForMessage, + EventLevel eventLevel, + int blockId, + CancellationToken cancellationToken) { - _pool = pool; - } + Debug.Assert(message != null || entityForMessage != null); - public void Construct(FunctionId functionId, string message, int blockId, CancellationToken cancellationToken) - { _functionId = functionId; - _tick = Environment.TickCount; + _message = message; + _entityForMessage = entityForMessage; + _eventLevel = eventLevel; _blockId = blockId; _cancellationToken = cancellationToken; + _tick = Environment.TickCount; + _startLogged = false; + } - Instance.BlockStart(message, functionId, blockId); + public static LogBlock Create( + FunctionId functionId, + object entityForMessage, + EventLevel eventLevel, + CancellationToken cancellationToken) + { + var blockId = GetNextUniqueBlockId(); + var logBlock = new LogBlock(functionId, message: null, entityForMessage, eventLevel, blockId, cancellationToken); + logBlock.OnStart(); + return logBlock; + } + + public static LogBlock Create( + FunctionId functionId, + string message, + EventLevel eventLevel, + CancellationToken cancellationToken) + { + var blockId = GetNextUniqueBlockId(); + var logBlock = new LogBlock(functionId, message, entityForMessage: null, eventLevel, blockId, cancellationToken); + logBlock.OnStart(); + return logBlock; + } + + /// + /// return next unique pair id + /// + private static int GetNextUniqueBlockId() + { + return Interlocked.Increment(ref s_lastUniqueBlockId); + } + + private void OnStart() + { + if (EnsureMessageIfLoggingEnabled()) + { + Debug.Assert(_message != null); + Debug.Assert(!_startLogged); + + Instance.BlockStart(_message, _functionId, _blockId); + _startLogged = true; + } + } + + private bool EnsureMessageIfLoggingEnabled() + { + if (Instance.IsEnabled(_eventLevel, EventKeywords.None)) + { + _message ??= (_entityForMessage?.ToString() ?? string.Empty); + return true; + } + + return false; } public void Dispose() { + if (!EnsureMessageIfLoggingEnabled()) + { + return; + } + + if (!_startLogged) + { + // User enabled logging after the block start. + // We log a block start to log the message along with the block ID. + Instance.BlockStart(_message, _functionId, _blockId); + _startLogged = true; + } + + Debug.Assert(_message != null); + // This delta is valid for durations of < 25 days var delta = Environment.TickCount - _tick; @@ -96,9 +156,6 @@ public void Dispose() { Instance.BlockStop(_functionId, delta, _blockId); } - - // Free this block back to the pool - _pool.Free(this); } } } From 95990dc430347a0953012d0ffdfd1c779541430b Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 23 Oct 2019 10:46:43 -0700 Subject: [PATCH 3/7] Revert some more changes --- .../Core/Portable/CodeRefactorings/CodeRefactoringService.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index 1e7636b5a5d62..c30dd717823f6 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -104,11 +104,9 @@ async Task> ICodeRefactoringService.GetRefactori bool isBlocking, CancellationToken cancellationToken) { - var extensionManager = document.Project.Solution.Workspace.Services.GetService(); - var isPerProviderLoggingEnabled = RoslynEventSource.Instance.IsEnabled(EventLevel.Informational, EventKeywords.None); - using (Logger.LogBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, cancellationToken)) { + var extensionManager = document.Project.Solution.Workspace.Services.GetService(); var tasks = new List>(); foreach (var provider in GetProviders(document)) From bfc9cd99693669de0d2225b5246ced4929b547b6 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 23 Oct 2019 10:50:14 -0700 Subject: [PATCH 4/7] Only log information for code fixes that can fix the diagnostics. --- .../Implementation/CodeFixes/CodeFixService.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs index 5fb68e54727e4..fafb721701936 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs @@ -300,12 +300,12 @@ private async Task AppendFixesAsync( { cancellationToken.ThrowIfCancellationRequested(); - using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixer, cancellationToken)) - { - await AppendFixesOrConfigurationsAsync( - document, span, diagnostics, fixAllForInSpan, result, fixer, - hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), - getFixes: dxs => + await AppendFixesOrConfigurationsAsync( + document, span, diagnostics, fixAllForInSpan, result, fixer, + hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), + getFixes: dxs => + { + using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixer, cancellationToken)) { if (fixAllForInSpan) { @@ -316,9 +316,9 @@ await AppendFixesOrConfigurationsAsync( { return GetCodeFixesAsync(document, span, fixer, isBlocking, dxs, cancellationToken); } - }, - cancellationToken: cancellationToken).ConfigureAwait(false); - } + } + }, + cancellationToken: cancellationToken).ConfigureAwait(false); // Just need the first result if we are doing fix all in span if (fixAllForInSpan && result.Any()) return; From 78e5e9f9a55c77f69263d84759d4668a61a0ba00 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 23 Oct 2019 10:52:45 -0700 Subject: [PATCH 5/7] Remove unnecessary usings --- .../Core/Implementation/CodeFixes/CodeFixService.cs | 1 - .../Core/Portable/CodeRefactorings/CodeRefactoringService.cs | 1 - ...ractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs index fafb721701936..7c8f15b609926 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics; -using System.Diagnostics.Tracing; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index c30dd717823f6..c3f07fe749c84 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics.Tracing; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs index 91fc2fb2b209d..3451c01948360 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.Tracing; using System.IO; using System.Text; using System.Threading; From 638a818a97f57275c3066866602601cceca77905 Mon Sep 17 00:00:00 2001 From: Ivan Basov Date: Thu, 24 Oct 2019 15:07:56 -0700 Subject: [PATCH 6/7] Variable suggestions show duplicated filter buttons on completion window (#39499) --- .../IntelliSense/AsyncCompletion/FilterSet.cs | 24 ++++++++++++++----- .../AbstractCompletionProviderTests.cs | 3 +++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/FilterSet.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/FilterSet.cs index 83ea431708f8f..5a1c067c5c5a5 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/FilterSet.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/FilterSet.cs @@ -25,10 +25,16 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncComplet internal sealed class FilterSet { // Cache all the VS completion filters which essentially make them singletons. - // Because all items that should be filtered using the same filter button must - // use the same reference to the instance of CompletionFilter. + // Need to map item tags such as Class, Interface, Local, Enum to filter buttons. + // There can be tags mapping to the same button: + // Local -> Locals and Parameters, Parameter -> Locals and Parameters. private static readonly ImmutableDictionary s_filterMap; + // Distinct list of all filters. + // Need to iterate over a distinct list of filters + // to create a filter list covering a completion session. + private static readonly ImmutableArray s_filters; + private BitVector32 _vector; private static readonly int s_expanderMask; @@ -54,7 +60,9 @@ internal sealed class FilterSet static FilterSet() { - var builder = ImmutableDictionary.CreateBuilder(); + var mapBuilder = ImmutableDictionary.CreateBuilder(); + var arrayBuilder = ImmutableArray.CreateBuilder(); + var previousMask = 0; NamespaceFilter = CreateCompletionFilterAndAddToBuilder(FeaturesResources.Namespaces, 'n', WellKnownTags.Namespace); @@ -75,7 +83,8 @@ static FilterSet() SnippetFilter = CreateCompletionFilterAndAddToBuilder(FeaturesResources.Snippets, 't', WellKnownTags.Snippet); TargetTypedFilter = CreateCompletionFilterAndAddToBuilder(FeaturesResources.Target_type_matches, 'j', WellKnownTags.TargetTypeMatch); - s_filterMap = builder.ToImmutable(); + s_filterMap = mapBuilder.ToImmutable(); + s_filters = arrayBuilder.ToImmutable(); s_expanderMask = BitVector32.CreateMask(previousMask); @@ -91,9 +100,12 @@ CompletionFilter CreateCompletionFilterAndAddToBuilder(string displayText, char var filter = CreateCompletionFilter(displayText, tags, accessKey); previousMask = BitVector32.CreateMask(previousMask); + var filterWithMask = new FilterWithMask(filter, previousMask); + arrayBuilder.Add(filterWithMask); + foreach (var tag in tags) { - builder.Add(tag, new FilterWithMask(filter, previousMask)); + mapBuilder.Add(tag, filterWithMask); } return filter; @@ -173,7 +185,7 @@ public ImmutableArray GetFilterStatesInSet(bool addUn builder.Add(new CompletionFilterWithState(Expander, isAvailable: true, isSelected: false)); } - foreach (var filterWithMask in s_filterMap.Values) + foreach (var filterWithMask in s_filters) { if (_vector[filterWithMask.Mask]) { diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 6da1edefa3f03..e040fd8e0a76b 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -181,6 +181,9 @@ protected void SetExperimentOption(string experimentName, bool enabled) private bool FiltersMatch(List expectedMatchingFilters, RoslynCompletion.CompletionItem item) { var matchingFilters = FilterSet.GetFilters(item); + + // Check that the list has no duplicates. + Assert.Equal(matchingFilters.Count, matchingFilters.Distinct().Count()); return expectedMatchingFilters.SetEquals(matchingFilters); } From 1713e0a9fcd41e777e0f4396723fe8dac65af265 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 24 Oct 2019 15:23:45 -0700 Subject: [PATCH 7/7] Address feedback --- .../Core/Portable/Log/RoslynEventSource.LogBlock.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs b/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs index d2042f6311ff8..0b4098ce13368 100644 --- a/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs +++ b/src/Workspaces/Core/Portable/Log/RoslynEventSource.LogBlock.cs @@ -19,7 +19,7 @@ internal partial class RoslynEventSource /// if the specified was explicitly enabled. /// Instead it checks if the was enabled at level. /// - public static IDisposable LogInformationalBlock(FunctionId functionId, object entity, CancellationToken cancellationToken) + public static LogBlock LogInformationalBlock(FunctionId functionId, object entity, CancellationToken cancellationToken) => LogBlock.Create(functionId, entity, EventLevel.Informational, cancellationToken); /// @@ -29,14 +29,14 @@ public static IDisposable LogInformationalBlock(FunctionId functionId, object en /// if the specified was explicitly enabled. /// Instead it checks if the was enabled at level. /// - public static IDisposable LogInformationalBlock(FunctionId functionId, string message, CancellationToken cancellationToken) + public static LogBlock LogInformationalBlock(FunctionId functionId, string message, CancellationToken cancellationToken) => LogBlock.Create(functionId, message, EventLevel.Informational, cancellationToken); /// /// This tracks the logged message. On instantiation, it logs 'Started block' with other event data. /// On dispose, it logs 'Ended block' with the same event data so we can track which block started and ended when looking at logs. /// - private struct LogBlock : IDisposable + internal struct LogBlock : IDisposable { private readonly FunctionId _functionId; private readonly object? _entityForMessage; @@ -135,6 +135,8 @@ public void Dispose() return; } + Debug.Assert(_message != null); + if (!_startLogged) { // User enabled logging after the block start. @@ -143,8 +145,6 @@ public void Dispose() _startLogged = true; } - Debug.Assert(_message != null); - // This delta is valid for durations of < 25 days var delta = Environment.TickCount - _tick;