Skip to content

Commit

Permalink
Remove potential partial snapshot in codefixservice
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi committed Feb 16, 2022
1 parent 25a439f commit 02603da
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 13 deletions.
32 changes: 23 additions & 9 deletions src/Features/Core/Portable/CodeFixes/CodeFixService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ internal partial class CodeFixService : ICodeFixService

private readonly IDiagnosticAnalyzerService _diagnosticService;
private readonly ImmutableArray<Lazy<CodeFixProvider, CodeChangeProviderMetadata>> _fixers;
private readonly Dictionary<string, List<Lazy<CodeFixProvider, CodeChangeProviderMetadata>>> _fixersPerLanguageMap;
private readonly ImmutableDictionary<string, ImmutableArray<Lazy<CodeFixProvider, CodeChangeProviderMetadata>>> _fixersPerLanguageMap;

private readonly ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, ImmutableDictionary<DiagnosticId, List<CodeFixProvider>>> _projectFixersMap = new();

// Shared by project fixers and workspace fixers.
private readonly Lazy<ImmutableDictionary<CodeFixProvider, CodeChangeProviderMetadata>> _lazyFixerToMetadataMap;
private readonly ConditionalWeakTable<AnalyzerReference, ProjectCodeFixProvider> _analyzerReferenceToFixersMap = new();
private readonly ConditionalWeakTable<AnalyzerReference, ProjectCodeFixProvider>.CreateValueCallback _createProjectCodeFixProvider = r => new ProjectCodeFixProvider(r);
private readonly ImmutableDictionary<LanguageKind, Lazy<ImmutableArray<IConfigurationFixProvider>>> _configurationProvidersMap;
Expand All @@ -56,6 +55,7 @@ internal partial class CodeFixService : ICodeFixService

private ImmutableDictionary<CodeFixProvider, ImmutableArray<DiagnosticId>> _fixerToFixableIdsMap = ImmutableDictionary<CodeFixProvider, ImmutableArray<DiagnosticId>>.Empty;
private ImmutableDictionary<object, FixAllProviderInfo?> _fixAllProviderMap = ImmutableDictionary<object, FixAllProviderInfo?>.Empty;
private ImmutableDictionary<CodeFixProvider, CodeChangeProviderMetadata?> _fixerToMetadataMap = ImmutableDictionary<CodeFixProvider, CodeChangeProviderMetadata?>.Empty;

[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
Expand All @@ -71,13 +71,9 @@ public CodeFixService(
_fixers = fixers.ToImmutableArray();
_fixersPerLanguageMap = _fixers.ToPerLanguageMapWithMultipleLanguages();

_lazyFixerToMetadataMap = new(() => fixers.Where(service => service.IsValueCreated).ToImmutableDictionary(service => service.Value, service => service.Metadata));

_configurationProvidersMap = GetConfigurationProvidersPerLanguageMap(configurationProviders);
}

private ImmutableDictionary<CodeFixProvider, CodeChangeProviderMetadata> FixerToMetadataMap => _lazyFixerToMetadataMap.Value;

public async Task<FirstDiagnosticResult> GetMostSevereFixableDiagnosticAsync(
Document document, TextSpan range, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -433,7 +429,7 @@ await AppendFixesOrConfigurationsAsync(
getFixes: dxs =>
{
var fixerName = fixer.GetType().Name;
FixerToMetadataMap.TryGetValue(fixer, out var fixerMetadata);
var fixerMetadata = TryGetMetadata(fixer);
using (addOperationScope(fixerName))
using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixerName, cancellationToken))
Expand Down Expand Up @@ -468,6 +464,24 @@ await AppendFixesOrConfigurationsAsync(
}
}

private CodeChangeProviderMetadata? TryGetMetadata(CodeFixProvider fixer)
{
return ImmutableInterlocked.GetOrAdd(
ref _fixerToMetadataMap,
fixer,
static (fixer, fixers) =>
{
foreach (var lazy in fixers)
{
if (lazy.IsValueCreated && lazy.Value == fixer)
return lazy.Metadata;
}
return null;
},
_fixers);
}

private static async Task<ImmutableArray<CodeFix>> GetCodeFixesAsync(
Document document, TextSpan span, CodeFixProvider fixer, CodeChangeProviderMetadata? fixerMetadata, CodeActionOptions options,
ImmutableArray<Diagnostic> diagnostics,
Expand Down Expand Up @@ -611,7 +625,7 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity)
}

// If the fix provider supports fix all occurrences, then get the corresponding FixAllProviderInfo and fix all context.
var fixAllProviderInfo = extensionManager.PerformFunction<FixAllProviderInfo?>(fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null);
var fixAllProviderInfo = extensionManager.PerformFunction(fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null);

FixAllState? fixAllState = null;
var supportedScopes = ImmutableArray<FixAllScope>.Empty;
Expand Down Expand Up @@ -890,7 +904,7 @@ private static ImmutableDictionary<LanguageKind, Lazy<ImmutableArray<IConfigurat

return configurationFixerMap.ToImmutable();

static ImmutableArray<IConfigurationFixProvider> GetConfigurationFixProviders(List<Lazy<IConfigurationFixProvider, CodeChangeProviderMetadata>> languageKindAndFixers)
static ImmutableArray<IConfigurationFixProvider> GetConfigurationFixProviders(ImmutableArray<Lazy<IConfigurationFixProvider, CodeChangeProviderMetadata>> languageKindAndFixers)
{
using var builderDisposer = ArrayBuilder<IConfigurationFixProvider>.GetInstance(out var builder);
var orderedLanguageKindAndFixers = ExtensionOrderer.Order(languageKindAndFixers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,24 @@ public static ImmutableDictionary<string, ImmutableArray<Lazy<TInterface, TMetad
return builder.Select(kvp => new KeyValuePair<string, ImmutableArray<Lazy<TInterface, TMetadata>>>(kvp.Key, kvp.Value.ToImmutableAndFree())).ToImmutableDictionary();
}

public static Dictionary<string, List<Lazy<TInterface, TMetadata>>> ToPerLanguageMapWithMultipleLanguages<TInterface, TMetadata>(this IEnumerable<Lazy<TInterface, TMetadata>> services)
public static ImmutableDictionary<string, ImmutableArray<Lazy<TInterface, TMetadata>>> ToPerLanguageMapWithMultipleLanguages<TInterface, TMetadata>(this IEnumerable<Lazy<TInterface, TMetadata>> services)
where TMetadata : ILanguagesMetadata
{
var map = new Dictionary<string, List<Lazy<TInterface, TMetadata>>>();
using var _ = PooledDictionary<string, ArrayBuilder<Lazy<TInterface, TMetadata>>>.GetInstance(out var map);

foreach (var service in services)
{
foreach (var language in service.Metadata.Languages.Distinct())
{
if (!string.IsNullOrEmpty(language))
{
var list = map.GetOrAdd(language, _ => new List<Lazy<TInterface, TMetadata>>());
var list = map.GetOrAdd(language, _ => ArrayBuilder<Lazy<TInterface, TMetadata>>.GetInstance());
list.Add(service);
}
}
}

return map;
return map.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutableAndFree());
}
}
}

0 comments on commit 02603da

Please sign in to comment.