Skip to content

Commit

Permalink
Merge pull request #46740 from jasonmalinowski/add-editorconfig-cache
Browse files Browse the repository at this point in the history
Add a cache for AnalyzerConfigSets
  • Loading branch information
msftbot[bot] authored Aug 14, 2020
2 parents b60aa8f + 7480979 commit c56c421
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Collections.Concurrent;

namespace Microsoft.CodeAnalysis
{
internal sealed class CachingAnalyzerConfigSet
{
private readonly ConcurrentDictionary<string, AnalyzerConfigOptionsResult> _sourcePathToResult = new ConcurrentDictionary<string, AnalyzerConfigOptionsResult>();
private readonly Func<string, AnalyzerConfigOptionsResult> _computeFunction;
private readonly AnalyzerConfigSet _underlyingSet;

public CachingAnalyzerConfigSet(AnalyzerConfigSet underlyingSet)
{
_underlyingSet = underlyingSet;
_computeFunction = _underlyingSet.GetOptionsForSourcePath;
}

public AnalyzerConfigOptionsResult GetOptionsForSourcePath(string sourcePath)
{
return _sourcePathToResult.GetOrAdd(sourcePath, _computeFunction);
}
}
}
31 changes: 20 additions & 11 deletions src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable enable

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
Expand Down Expand Up @@ -55,7 +56,7 @@ internal partial class ProjectState
/// <summary>
/// The <see cref="AnalyzerConfigSet"/> to be used for analyzer options for specific trees.
/// </summary>
private readonly ValueSource<AnalyzerConfigSet> _lazyAnalyzerConfigSet;
private readonly ValueSource<CachingAnalyzerConfigSet> _lazyAnalyzerConfigSet;

private AnalyzerOptions? _lazyAnalyzerOptions;

Expand All @@ -70,7 +71,7 @@ private ProjectState(
ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> analyzerConfigDocumentStates,
AsyncLazy<VersionStamp> lazyLatestDocumentVersion,
AsyncLazy<VersionStamp> lazyLatestDocumentTopLevelChangeVersion,
ValueSource<AnalyzerConfigSet> lazyAnalyzerConfigSet)
ValueSource<CachingAnalyzerConfigSet> lazyAnalyzerConfigSet)
{
_solutionServices = solutionServices;
_languageServices = languageServices;
Expand Down Expand Up @@ -321,6 +322,7 @@ public override AnalyzerConfigOptions GetOptions(AdditionalText textFile)
// TODO: correctly find the file path, since it looks like we give this the document's .Name under the covers if we don't have one
return new WorkspaceAnalyzerConfigOptions(_projectState._lazyAnalyzerConfigSet.GetValue(CancellationToken.None).GetOptionsForSourcePath(textFile.Path));
}

private sealed class WorkspaceAnalyzerConfigOptions : AnalyzerConfigOptions
{
private readonly ImmutableDictionary<string, string> _backing;
Expand All @@ -334,9 +336,9 @@ public WorkspaceAnalyzerConfigOptions(AnalyzerConfigOptionsResult analyzerConfig

private sealed class WorkspaceSyntaxTreeOptionsProvider : SyntaxTreeOptionsProvider
{
private readonly ValueSource<AnalyzerConfigSet> _lazyAnalyzerConfigSet;
private readonly ValueSource<CachingAnalyzerConfigSet> _lazyAnalyzerConfigSet;

public WorkspaceSyntaxTreeOptionsProvider(ValueSource<AnalyzerConfigSet> lazyAnalyzerConfigSet)
public WorkspaceSyntaxTreeOptionsProvider(ValueSource<CachingAnalyzerConfigSet> lazyAnalyzerConfigSet)
=> _lazyAnalyzerConfigSet = lazyAnalyzerConfigSet;

public override bool? IsGenerated(SyntaxTree tree)
Expand All @@ -362,22 +364,22 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _lazyAnalyzerConfigSet.GetHashCode();
}

private static ValueSource<AnalyzerConfigSet> ComputeAnalyzerConfigSetValueSource(IEnumerable<AnalyzerConfigDocumentState> analyzerConfigDocumentStates)
private static ValueSource<CachingAnalyzerConfigSet> ComputeAnalyzerConfigSetValueSource(IEnumerable<AnalyzerConfigDocumentState> analyzerConfigDocumentStates)
{
return new AsyncLazy<AnalyzerConfigSet>(
return new AsyncLazy<CachingAnalyzerConfigSet>(
asynchronousComputeFunction: async cancellationToken =>
{
var tasks = analyzerConfigDocumentStates.Select(a => a.GetAnalyzerConfigAsync(cancellationToken));
var analyzerConfigs = await Task.WhenAll(tasks).ConfigureAwait(false);

cancellationToken.ThrowIfCancellationRequested();

return AnalyzerConfigSet.Create(analyzerConfigs);
return new CachingAnalyzerConfigSet(AnalyzerConfigSet.Create(analyzerConfigs));
},
synchronousComputeFunction: cancellationToken =>
{
var analyzerConfigs = analyzerConfigDocumentStates.SelectAsArray(a => a.GetAnalyzerConfig(cancellationToken));
return AnalyzerConfigSet.Create(analyzerConfigs);
return new CachingAnalyzerConfigSet(AnalyzerConfigSet.Create(analyzerConfigs));
},
cacheResult: true);
}
Expand Down Expand Up @@ -522,7 +524,7 @@ private ProjectState With(
ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState>? analyzerConfigDocumentStates = null,
AsyncLazy<VersionStamp>? latestDocumentVersion = null,
AsyncLazy<VersionStamp>? latestDocumentTopLevelChangeVersion = null,
ValueSource<AnalyzerConfigSet>? analyzerConfigSet = null)
ValueSource<CachingAnalyzerConfigSet>? analyzerConfigSet = null)
{
return new ProjectState(
projectInfo ?? _projectInfo,
Expand Down Expand Up @@ -695,6 +697,7 @@ private ProjectState CreateNewStateForChangedAnalyzerConfigDocuments(ImmutableSo
projectInfo = projectInfo
.WithCompilationOptions(CompilationOptions.WithSyntaxTreeOptionsProvider(newProvider));
}

return this.With(
projectInfo: projectInfo,
analyzerConfigDocumentStates: newAnalyzerConfigDocumentStates,
Expand All @@ -703,10 +706,13 @@ private ProjectState CreateNewStateForChangedAnalyzerConfigDocuments(ImmutableSo

public ProjectState RemoveDocuments(ImmutableArray<DocumentId> documentIds)
{
// We create a new CachingAnalyzerConfigSet for the new snapshot to avoid holding onto cached information
// for removed documents.
return this.With(
projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
documentIds: _documentIds.RemoveRange(documentIds),
documentStates: _documentStates.RemoveRange(documentIds));
documentStates: _documentStates.RemoveRange(documentIds),
analyzerConfigSet: ComputeAnalyzerConfigSetValueSource(AnalyzerConfigDocumentStates.Values));
}

public ProjectState RemoveAdditionalDocuments(ImmutableArray<DocumentId> documentIds)
Expand All @@ -726,10 +732,13 @@ public ProjectState RemoveAnalyzerConfigDocuments(ImmutableArray<DocumentId> doc

public ProjectState RemoveAllDocuments()
{
// We create a new CachingAnalyzerConfigSet for the new snapshot to avoid holding onto cached information
// for removed documents.
return this.With(
projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()).WithDocuments(SpecializedCollections.EmptyEnumerable<DocumentInfo>()),
documentIds: ImmutableList<DocumentId>.Empty,
documentStates: ImmutableSortedDictionary.Create<DocumentId, DocumentState>(DocumentIdComparer.Instance));
documentStates: ImmutableSortedDictionary.Create<DocumentId, DocumentState>(DocumentIdComparer.Instance),
analyzerConfigSet: ComputeAnalyzerConfigSetValueSource(AnalyzerConfigDocumentStates.Values));
}

public ProjectState UpdateDocument(DocumentState newDocument, bool textChanged, bool recalculateDependentVersions)
Expand Down

0 comments on commit c56c421

Please sign in to comment.