Skip to content

Commit 410c349

Browse files
Fix issue where we were crashing trying to get inheritance margin information for SG docs (#77356)
2 parents 4147458 + c614a02 commit 410c349

File tree

1 file changed

+29
-8
lines changed

1 file changed

+29
-8
lines changed

src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Runtime.CompilerServices;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using Microsoft.CodeAnalysis.Diagnostics;
910
using Microsoft.CodeAnalysis.LanguageService;
1011
using Microsoft.CodeAnalysis.Storage;
1112
using Roslyn.Utilities;
@@ -21,26 +22,46 @@ private sealed class ProjectIndex(
2122
MultiDictionary<DocumentId, DeclaredSymbolInfo> delegates,
2223
MultiDictionary<string, (DocumentId, DeclaredSymbolInfo)> namedTypes)
2324
{
24-
private static readonly ConditionalWeakTable<ProjectState, AsyncLazy<ProjectIndex>> s_projectToIndex = new();
25+
/// <summary>
26+
/// We cache the project instance per <see cref="ProjectState"/>. This allows us to reuse it over a wide set of
27+
/// changes (for example, changing completely unrelated projects that a particular project doesn't depend on).
28+
/// However, <see cref="ProjectState"/> doesn't change even when certain things change that will create a
29+
/// substantively different <see cref="Project"/>. For example, if the <see
30+
/// cref="SourceGeneratorExecutionVersion"/> for the project changes, we'll still have the same project state.
31+
/// As such, we store the <see cref="Checksum"/> of the project as well, ensuring that if anything in it or its
32+
/// dependencies changes, we recompute the index.
33+
/// </summary>
34+
private static readonly ConditionalWeakTable<ProjectState, StrongBox<(Checksum checksum, AsyncLazy<ProjectIndex> lazyProjectIndex)>> s_projectToIndex = new();
2535

2636
public readonly MultiDictionary<DocumentId, DeclaredSymbolInfo> ClassesAndRecordsThatMayDeriveFromSystemObject = classesAndRecordsThatMayDeriveFromSystemObject;
2737
public readonly MultiDictionary<DocumentId, DeclaredSymbolInfo> ValueTypes = valueTypes;
2838
public readonly MultiDictionary<DocumentId, DeclaredSymbolInfo> Enums = enums;
2939
public readonly MultiDictionary<DocumentId, DeclaredSymbolInfo> Delegates = delegates;
3040
public readonly MultiDictionary<string, (DocumentId, DeclaredSymbolInfo)> NamedTypes = namedTypes;
3141

32-
public static Task<ProjectIndex> GetIndexAsync(
42+
public static async Task<ProjectIndex> GetIndexAsync(
3343
Project project, CancellationToken cancellationToken)
3444
{
35-
if (!s_projectToIndex.TryGetValue(project.State, out var lazyIndex))
45+
// Use the checksum of the project. That way if its state *or* SG info changes, we compute a new index with
46+
// accurate information in it.
47+
var checksum = await project.GetDiagnosticChecksumAsync(cancellationToken).ConfigureAwait(false);
48+
if (!s_projectToIndex.TryGetValue(project.State, out var tuple) ||
49+
tuple.Value.checksum != checksum)
3650
{
37-
lazyIndex = s_projectToIndex.GetValue(
38-
project.State, p => AsyncLazy.Create(
39-
static (project, c) => CreateIndexAsync(project, c),
40-
project));
51+
tuple = new((checksum, AsyncLazy.Create(CreateIndexAsync, project)));
52+
53+
#if NET
54+
s_projectToIndex.AddOrUpdate(project.State, tuple);
55+
#else
56+
// Best effort try to update the map with the new data.
57+
s_projectToIndex.Remove(project.State);
58+
// Note: intentionally ignore the return value here. We want to use the value we've computed even if
59+
// another thread beats us to adding things here.
60+
_ = s_projectToIndex.GetValue(project.State, _ => tuple);
61+
#endif
4162
}
4263

43-
return lazyIndex.GetValueAsync(cancellationToken);
64+
return await tuple.Value.lazyProjectIndex.GetValueAsync(cancellationToken).ConfigureAwait(false);
4465
}
4566

4667
private static async Task<ProjectIndex> CreateIndexAsync(Project project, CancellationToken cancellationToken)

0 commit comments

Comments
 (0)