Skip to content

Load analyzers and generators in isolated ALCs in our OOP process #74780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 191 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
191 commits
Select commit Hold shift + click to select a range
4a74960
In progress
CyrusNajmabadi Aug 15, 2024
9b15720
in progress
CyrusNajmabadi Aug 15, 2024
fcd2791
in progress
CyrusNajmabadi Aug 15, 2024
476346f
in progress
CyrusNajmabadi Aug 15, 2024
eb9e531
Finish up
CyrusNajmabadi Aug 15, 2024
4f33bb9
in progress
CyrusNajmabadi Aug 15, 2024
1fcd8fa
in progress
CyrusNajmabadi Aug 15, 2024
6598832
Add back
CyrusNajmabadi Aug 15, 2024
eda27b0
dedicated file
CyrusNajmabadi Aug 15, 2024
6276106
in progress
CyrusNajmabadi Aug 15, 2024
ee79e75
in progress
CyrusNajmabadi Aug 15, 2024
ef8c9b3
simplify
CyrusNajmabadi Aug 15, 2024
bb30403
Revert
CyrusNajmabadi Aug 15, 2024
40d111f
Share more
CyrusNajmabadi Aug 15, 2024
72527b7
docs
CyrusNajmabadi Aug 15, 2024
9c5ce93
Docs
CyrusNajmabadi Aug 15, 2024
df1fa06
Docs
CyrusNajmabadi Aug 15, 2024
e1c8108
Docs
CyrusNajmabadi Aug 15, 2024
abf7d7d
Unique path
CyrusNajmabadi Aug 15, 2024
e5508fd
Doc
CyrusNajmabadi Aug 15, 2024
d82d2a9
Simplify
CyrusNajmabadi Aug 15, 2024
0000865
Change ifdefs
CyrusNajmabadi Aug 15, 2024
46931b1
Change ifdefs
CyrusNajmabadi Aug 15, 2024
b943bf8
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 19, 2024
cf71ad8
in progress
CyrusNajmabadi Aug 19, 2024
e0b00d5
move
CyrusNajmabadi Aug 19, 2024
cdf0aed
fix
CyrusNajmabadi Aug 19, 2024
e650f96
Simplify
CyrusNajmabadi Aug 19, 2024
682c455
Simplify
CyrusNajmabadi Aug 19, 2024
6bbd6aa
move to common location
CyrusNajmabadi Aug 19, 2024
a729ef1
Move to shared location
CyrusNajmabadi Aug 20, 2024
3cf946d
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 20, 2024
9fd7fc8
Docs
CyrusNajmabadi Aug 20, 2024
3dcf644
Docs
CyrusNajmabadi Aug 20, 2024
a83ebc5
Docs
CyrusNajmabadi Aug 20, 2024
4925999
Docs
CyrusNajmabadi Aug 20, 2024
84a60b6
Similar logic
CyrusNajmabadi Aug 20, 2024
34db05b
Sharing
CyrusNajmabadi Aug 20, 2024
6ba258a
Docs
CyrusNajmabadi Aug 20, 2024
8e70d06
restore
CyrusNajmabadi Aug 20, 2024
b771f97
Docs
CyrusNajmabadi Aug 20, 2024
df5764f
Docs
CyrusNajmabadi Aug 20, 2024
514c0b4
Docs
CyrusNajmabadi Aug 20, 2024
306e783
not abstract
CyrusNajmabadi Aug 20, 2024
658abfa
renames
CyrusNajmabadi Aug 20, 2024
8eda644
Docs
CyrusNajmabadi Aug 20, 2024
4cf1330
Merge remote-tracking branch 'dibarbet/fix_design_time_build' into is…
CyrusNajmabadi Aug 20, 2024
730c883
in progress
CyrusNajmabadi Aug 20, 2024
e6a3883
Make protected
CyrusNajmabadi Aug 20, 2024
91d97dc
simplify
CyrusNajmabadi Aug 20, 2024
78c734e
Fix
CyrusNajmabadi Aug 20, 2024
217a496
Fix
CyrusNajmabadi Aug 20, 2024
a7959ce
primary constructor
CyrusNajmabadi Aug 20, 2024
a6c5e97
Merge branch 'checksummedObject' into isolatedALC
CyrusNajmabadi Aug 20, 2024
abc6af5
Fix
CyrusNajmabadi Aug 20, 2024
8661560
Fix
CyrusNajmabadi Aug 20, 2024
bd8f926
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 21, 2024
5b481d6
Fix
CyrusNajmabadi Aug 21, 2024
a07bb12
Jared pattern
CyrusNajmabadi Aug 21, 2024
8d2d21a
Revert
CyrusNajmabadi Aug 21, 2024
694080c
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 21, 2024
fe82439
revert
CyrusNajmabadi Aug 21, 2024
3fff183
revert
CyrusNajmabadi Aug 21, 2024
14a7ff4
no default
CyrusNajmabadi Aug 21, 2024
01d109e
Update src/Workspaces/CoreTestUtilities/Remote/TestSerializerService.cs
CyrusNajmabadi Aug 21, 2024
7c001e5
Simplify
CyrusNajmabadi Aug 21, 2024
d4450d9
Wrapping
CyrusNajmabadi Aug 21, 2024
3f3e15c
contract messages
CyrusNajmabadi Aug 21, 2024
ae16400
Rename
CyrusNajmabadi Aug 21, 2024
39bcc6e
Add gc
CyrusNajmabadi Aug 21, 2024
dfc26e0
move code
CyrusNajmabadi Aug 21, 2024
f534e72
Collection expression
CyrusNajmabadi Aug 21, 2024
32518fb
asserts
CyrusNajmabadi Aug 21, 2024
ea7f576
blank lines
CyrusNajmabadi Aug 21, 2024
6f5a76f
Sort usings
CyrusNajmabadi Aug 21, 2024
063d9a2
Make a counter instead
CyrusNajmabadi Aug 21, 2024
d105e2f
Merge branch 'main' into isolatedALC
CyrusNajmabadi Aug 21, 2024
7b7392a
Different approach
CyrusNajmabadi Aug 21, 2024
fd6fb98
in progress
CyrusNajmabadi Aug 21, 2024
65620d2
in progress
CyrusNajmabadi Aug 21, 2024
b597d4a
Cleanup
CyrusNajmabadi Aug 21, 2024
a478c33
Trivially bail out
CyrusNajmabadi Aug 21, 2024
07b0497
File change notifications working
CyrusNajmabadi Aug 21, 2024
f554f3e
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 22, 2024
e9d636d
Merge branch 'moveType' into isolatedALC
CyrusNajmabadi Aug 22, 2024
362cf63
Merge branch 'moveType' into isolatedALC
CyrusNajmabadi Aug 22, 2024
10af3cf
Merge branch 'analyzerWatching' into isolatedALC
CyrusNajmabadi Aug 22, 2024
ae1c683
Make static
CyrusNajmabadi Aug 22, 2024
23307e0
Merge branch 'analyzerWatching' into isolatedALC
CyrusNajmabadi Aug 22, 2024
e5711f8
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 22, 2024
f224ba0
Merge branch 'unloadAll' into isolatedALC
CyrusNajmabadi Aug 22, 2024
a960412
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 22, 2024
fa01f64
Merge branch 'batching' into isolatedALC
CyrusNajmabadi Aug 22, 2024
5a69e9c
in progresS
CyrusNajmabadi Aug 22, 2024
3c7e326
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 22, 2024
18b5893
in progress
CyrusNajmabadi Aug 22, 2024
f6cd0bd
Rename
CyrusNajmabadi Aug 22, 2024
efda682
Remove
CyrusNajmabadi Aug 22, 2024
a3ebfc9
Working test
CyrusNajmabadi Aug 22, 2024
f01eaf8
Renames
CyrusNajmabadi Aug 22, 2024
e801da2
Merge branch 'main' into isolatedALC
CyrusNajmabadi Aug 23, 2024
01e8e65
Update src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssembl…
CyrusNajmabadi Aug 23, 2024
c53f25a
Cleanup api
CyrusNajmabadi Aug 23, 2024
24b7698
keealive
CyrusNajmabadi Aug 23, 2024
2aaa1bc
Merge branch 'main' into isolatedALC
CyrusNajmabadi Aug 23, 2024
ef90315
in progress
CyrusNajmabadi Aug 23, 2024
1352fff
Merge branch 'stringFileWatching' into isolatedALC
CyrusNajmabadi Aug 23, 2024
b31dca4
in progress
CyrusNajmabadi Aug 23, 2024
b030aa4
in progress
CyrusNajmabadi Aug 23, 2024
f836f16
in progress
CyrusNajmabadi Aug 23, 2024
ae5aa2e
in progress
CyrusNajmabadi Aug 23, 2024
5c9d7b4
in progress
CyrusNajmabadi Aug 23, 2024
188e44d
Simplify
CyrusNajmabadi Aug 23, 2024
f44e95f
more work
CyrusNajmabadi Aug 23, 2024
e637c25
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 23, 2024
57152d8
in progress
CyrusNajmabadi Aug 23, 2024
da0e43e
Move and rename
CyrusNajmabadi Aug 23, 2024
d3d3866
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 23, 2024
587b6c3
Downsream fallout
CyrusNajmabadi Aug 23, 2024
bcb33a8
Tests
CyrusNajmabadi Aug 23, 2024
9470752
Use new context
CyrusNajmabadi Aug 23, 2024
f851590
move back
CyrusNajmabadi Aug 23, 2024
a5244a6
Docs
CyrusNajmabadi Aug 23, 2024
df51cbd
revert
CyrusNajmabadi Aug 23, 2024
1bd7e08
REvert
CyrusNajmabadi Aug 23, 2024
1fa9eec
Simplify
CyrusNajmabadi Aug 23, 2024
c551d72
move
CyrusNajmabadi Aug 23, 2024
d688f8e
REvert
CyrusNajmabadi Aug 23, 2024
73fc9ff
REvert
CyrusNajmabadi Aug 23, 2024
523035e
in progress
CyrusNajmabadi Aug 23, 2024
a5964bd
partials
CyrusNajmabadi Aug 23, 2024
857f0e0
cleanup
CyrusNajmabadi Aug 23, 2024
5ed0d68
cleanup
CyrusNajmabadi Aug 23, 2024
c363a01
REvert
CyrusNajmabadi Aug 23, 2024
6854ae1
Sort
CyrusNajmabadi Aug 23, 2024
438db6f
REvert
CyrusNajmabadi Aug 23, 2024
55d9748
Sort
CyrusNajmabadi Aug 23, 2024
3124d1e
Remove
CyrusNajmabadi Aug 23, 2024
520508b
Merge branch 'passAlongServices' into isolatedALC
CyrusNajmabadi Aug 23, 2024
81d0fd3
Merge branch 'passAlongServices' into isolatedALC
CyrusNajmabadi Aug 23, 2024
2e2393e
REmove
CyrusNajmabadi Aug 23, 2024
28b0ca4
Use helper
CyrusNajmabadi Aug 23, 2024
d1a3c0d
in progress
CyrusNajmabadi Aug 23, 2024
7e871f6
in progress
CyrusNajmabadi Aug 23, 2024
abb4df4
In progress
CyrusNajmabadi Aug 23, 2024
393b5a2
in progress
CyrusNajmabadi Aug 23, 2024
20a88fa
In progress
CyrusNajmabadi Aug 23, 2024
79f701c
move to strings
CyrusNajmabadi Aug 23, 2024
390ada5
Don't go through the workspace
CyrusNajmabadi Aug 23, 2024
642ac91
Merge branch 'fileChangeWorkQueue' into isolatedALC
CyrusNajmabadi Aug 23, 2024
24bfad2
REvert
CyrusNajmabadi Aug 23, 2024
a1e46b6
Merge branch 'fileChangeWorkQueue' into isolatedALC
CyrusNajmabadi Aug 23, 2024
b99b396
Merge branch 'fileChangeWorkQueue' into isolatedALC
CyrusNajmabadi Aug 23, 2024
c4162c0
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 23, 2024
9dc6b7e
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 24, 2024
cc270ee
Update src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostW…
CyrusNajmabadi Aug 24, 2024
82f7120
only in net
CyrusNajmabadi Aug 24, 2024
f86a8aa
docs
CyrusNajmabadi Aug 24, 2024
a6b6d9e
Merge branch 'isolatedALC' of https://github.com/CyrusNajmabadi/rosly…
CyrusNajmabadi Aug 24, 2024
9ad33f7
move
CyrusNajmabadi Aug 24, 2024
4f961ff
rename
CyrusNajmabadi Aug 24, 2024
d0bfc65
docs
CyrusNajmabadi Aug 24, 2024
1975a1a
docs
CyrusNajmabadi Aug 24, 2024
7462d05
docs
CyrusNajmabadi Aug 24, 2024
266e439
Simplify logic
CyrusNajmabadi Aug 24, 2024
b823683
update test
CyrusNajmabadi Aug 24, 2024
ab4ba87
Fix comment
CyrusNajmabadi Aug 24, 2024
176b8a8
pass id
CyrusNajmabadi Aug 24, 2024
57aee0e
simplify
CyrusNajmabadi Aug 24, 2024
4758f3b
stop watching references
CyrusNajmabadi Aug 24, 2024
75a4955
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 25, 2024
a613d6a
Fix
CyrusNajmabadi Aug 25, 2024
a90f7ec
Share code
CyrusNajmabadi Aug 25, 2024
1ef5673
name our directory
CyrusNajmabadi Aug 25, 2024
cbf85eb
Merge branch 'loaderProvider' into isolatedALC
CyrusNajmabadi Aug 25, 2024
81afa79
Simplify
CyrusNajmabadi Aug 25, 2024
9e011e7
Merge branch 'loaderProvider' into isolatedALC
CyrusNajmabadi Aug 25, 2024
49435a4
Merge branch 'loaderProvider' into isolatedALC
CyrusNajmabadi Aug 25, 2024
14139ef
inline
CyrusNajmabadi Aug 25, 2024
c793b18
remove
CyrusNajmabadi Aug 25, 2024
9eba2a5
Merge branch 'loaderProvider' into isolatedALC
CyrusNajmabadi Aug 25, 2024
c03b6da
Docs
CyrusNajmabadi Aug 25, 2024
c622459
Merge branch 'loaderProvider' into isolatedALC
CyrusNajmabadi Aug 25, 2024
ae387e5
Merge branch 'loaderProvider' into isolatedALC
CyrusNajmabadi Aug 26, 2024
82ddb42
move into pp region
CyrusNajmabadi Aug 26, 2024
059e3ff
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 26, 2024
bd46648
remove
CyrusNajmabadi Aug 26, 2024
4b77dc5
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 26, 2024
9be2700
Update src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSy…
CyrusNajmabadi Aug 27, 2024
ca06773
Update src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGenera…
CyrusNajmabadi Aug 27, 2024
70c973c
Merge remote-tracking branch 'upstream/main' into isolatedALC
CyrusNajmabadi Aug 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ public LanguageServerWorkspaceFactory(
public async Task InitializeSolutionLevelAnalyzersAsync(ImmutableArray<string> analyzerPaths)
{
var references = new List<AnalyzerFileReference>();
var analyzerLoader = Workspace.Services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>().SharedShadowCopyLoader;
var loaderProvider = Workspace.Services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>();

// Load all analyzers into a fresh shadow copied load context. In the future, if we want to support reloading
// of solution-level analyzer references, we should just need to listen for changes to those analyzer paths and
// then call back into this method to update the solution accordingly.
var analyzerLoader = loaderProvider.CreateNewShadowCopyLoader();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dibarbet fyi

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: this is just for solution-level analyzers. Project level analyzers will reload automatically.

Copy link
Member

@dibarbet dibarbet Aug 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with me. Will have to reload VSCode to reload sln level analyzers, but those should be way way less common than project based ones.


foreach (var analyzerPath in analyzerPaths)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ public async Task<T> GetValueAsync<T>(Checksum checksum)

public async Task<Solution> GetSolutionAsync(SolutionAssetStorage.Scope scope)
{
var solutionInfo = await new AssetProvider(this).CreateSolutionInfoAsync(scope.SolutionChecksum, CancellationToken.None).ConfigureAwait(false);
var solutionInfo = await new AssetProvider(this).CreateSolutionInfoAsync(
scope.SolutionChecksum, this.Services.SolutionServices, CancellationToken.None).ConfigureAwait(false);

var workspace = new AdhocWorkspace(Services.HostServices);
return workspace.AddSolution(solutionInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Test;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Remote.Testing;
using Microsoft.CodeAnalysis.Serialization;
Expand Down Expand Up @@ -446,7 +447,10 @@ public async Task TestRemoteWorkspace()
await Verify(remoteWorkspace, currentSolution, remoteSolution3);

// move to new solution backward
var solutionInfo2 = await assetProvider.CreateSolutionInfoAsync(await solution1.CompilationState.GetChecksumAsync(CancellationToken.None), CancellationToken.None);
var solutionInfo2 = await assetProvider.CreateSolutionInfoAsync(
await solution1.CompilationState.GetChecksumAsync(CancellationToken.None),
remoteWorkspace.Services.SolutionServices,
CancellationToken.None);
var solution2 = remoteWorkspace.GetTestAccessor().CreateSolutionFromInfo(solutionInfo2);

// move to new solution forward
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection.Metadata;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
Expand Down Expand Up @@ -54,15 +53,24 @@ private static Checksum CreateChecksum(MetadataReference reference)

protected virtual Checksum CreateChecksum(AnalyzerReference reference)
{
#if NET
// If we're in the oop side and we're being asked to produce our local checksum (so we can compare it to the
// host checksum), then we want to just defer to the underlying analyzer reference of our isolated reference.
// This underlying reference corresponds to the reference that the host has, and we do not want to make any
// changes as long as they're both in agreement.
if (reference is IsolatedAnalyzerFileReference { UnderlyingAnalyzerFileReference: var underlyingReference })
reference = underlyingReference;
#endif

using var stream = SerializableBytes.CreateWritableStream();

using (var writer = new ObjectWriter(stream, leaveOpen: true))
{
switch (reference)
{
case AnalyzerFileReference file:
writer.WriteString(file.FullPath);
writer.WriteGuid(TryGetAnalyzerFileReferenceMvid(file));
case AnalyzerFileReference fileReference:
writer.WriteString(fileReference.FullPath);
writer.WriteGuid(TryGetAnalyzerFileReferenceMvid(fileReference));
break;

case AnalyzerImageReference analyzerImageReference:
Expand Down Expand Up @@ -109,11 +117,11 @@ protected virtual void WriteAnalyzerReferenceTo(AnalyzerReference reference, Obj
{
switch (reference)
{
case AnalyzerFileReference file:
case AnalyzerFileReference fileReference:
writer.WriteString(nameof(AnalyzerFileReference));
writer.WriteString(file.FullPath);
writer.WriteString(fileReference.FullPath);

// Note: it is intentional that we are not writing the MVID of the analyzer file reference over (even
// Note: it is intentional that we are not writing the MVID of the analyzer file reference over in (even
// though we mixed it into the checksum). We don't actually need the data on the other side as it will
// be read out from the file itself. So the flow is as follows when an analyzer-file-reference changes:
//
Expand Down Expand Up @@ -150,8 +158,10 @@ protected virtual AnalyzerReference ReadAnalyzerReferenceFrom(ObjectReader reade
switch (reader.ReadString())
{
case nameof(AnalyzerFileReference):
var fullPath = reader.ReadRequiredString();
return new AnalyzerFileReference(fullPath, _analyzerLoaderProvider.SharedShadowCopyLoader);
// Rehydrate the analyzer file reference with the simple shared shadow copy loader. Note: we won't
// actually use this instance we create. Instead, the caller will use create an IsolatedAssemblyReferenceSet
// from these to ensure that all the types can be safely loaded into their own ALC.
return new AnalyzerFileReference(reader.ReadRequiredString(), _analyzerLoaderProvider.SharedShadowCopyLoader);

case nameof(AnalyzerImageReference):
var guid = reader.ReadGuid();
Expand Down Expand Up @@ -286,7 +296,7 @@ private PortableExecutableReference ReadPortableExecutableReferenceFrom(ObjectRe
// so that we can put xml doc comment as part of snapshot. but until we believe that is necessary,
// it will go with simpler approach
var documentProvider = filePath != null && _documentationService != null ?
_documentationService.GetDocumentationProvider(filePath) : XmlDocumentationProvider.Default;
_documentationService.GetDocumentationProvider(filePath) : DocumentationProvider.Default;

return new SerializedPortableExecutableReference(
properties, filePath, metadata, storageHandles, documentProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,24 @@
using System.IO;
using Microsoft.CodeAnalysis.Host.Mef;

#if NET
using Microsoft.CodeAnalysis.Diagnostics;
using System.Runtime.Loader;
#endif

namespace Microsoft.CodeAnalysis.Host;

internal interface IAnalyzerAssemblyLoaderProvider : IWorkspaceService
{
IAnalyzerAssemblyLoaderInternal SharedShadowCopyLoader { get; }

#if NET
/// <summary>
/// Creates a fresh shadow copying loader that will load all <see cref="AnalyzerReference"/>s and <see
/// cref="ISourceGenerator"/>s in a fresh <see cref="AssemblyLoadContext"/>.
/// </summary>
IAnalyzerAssemblyLoaderInternal CreateNewShadowCopyLoader();
#endif
}

/// <summary>
Expand All @@ -28,13 +41,13 @@ internal abstract class AbstractAnalyzerAssemblyLoaderProvider : IAnalyzerAssemb
public AbstractAnalyzerAssemblyLoaderProvider(IEnumerable<IAnalyzerAssemblyResolver> externalResolvers)
{
_externalResolvers = externalResolvers.ToImmutableArray();
_shadowCopyLoader = new(CreateShadowCopyLoader);
_shadowCopyLoader = new(CreateNewShadowCopyLoader);
}

public IAnalyzerAssemblyLoaderInternal SharedShadowCopyLoader
=> _shadowCopyLoader.Value;

private IAnalyzerAssemblyLoaderInternal CreateShadowCopyLoader()
public IAnalyzerAssemblyLoaderInternal CreateNewShadowCopyLoader()
=> this.WrapLoader(DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(
Path.Combine(Path.GetTempPath(), nameof(Roslyn), "AnalyzerAssemblyLoader"),
_externalResolvers));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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.

#if NET

using System;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Runtime.Loader;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis;

/// <summary>
/// Wrapper around a real <see cref="AnalyzerFileReference"/>. An "isolated" analyzer reference is an analyzer
/// reference associated with an <see cref="AssemblyLoadContext"/> that is connected to a set of other "isolated"
/// analyzer references. This allows for loading the analyzers and generators from it in a way that is associated with
/// that load context, keeping them separate from other analyzers and generators loaded in other load contexts, while
/// also allowing all of those instances to be collected when no longer needed. Being isolated means that if any of the
/// underlying assembly references change, that they can be loaded side by side with the prior references. This enables
/// functionality like live reloading of analyzers and generators when they change on disk. Note: this is only
/// supported on .Net Core, and not .Net Framework, as only the former has <see cref="AssemblyLoadContext"/>s.
/// </summary>
/// <remarks>
/// The purpose of this type is to allow passing out a <see cref="AnalyzerReference"/> to the rest of the system that
/// then ensures that as long as it is alive (or any <see cref="DiagnosticAnalyzer"/> or <see cref="ISourceGenerator"/>
/// it passes out is alive), that the <see cref="IsolatedAnalyzerReferenceSet"/> (and its corresponding <see
/// cref="AssemblyLoadContext"/>) is kept alive as well.
/// </remarks>
internal sealed class IsolatedAnalyzerFileReference(
IsolatedAnalyzerReferenceSet isolatedAnalyzerReferenceSet,
AnalyzerFileReference underlyingAnalyzerReference)
: AnalyzerReference
{
/// <summary>
/// Conditional weak tables that ensure that as long as a particular <see cref="DiagnosticAnalyzer"/> or <see
/// cref="ISourceGenerator"/> is alive, that the corresponding <see cref="IsolatedAnalyzerReferenceSet"/> (and its
/// corresponding <see cref="AssemblyLoadContext"/> is kept alive.
/// </summary>
private static readonly ConditionalWeakTable<DiagnosticAnalyzer, IsolatedAnalyzerReferenceSet> s_analyzerToPinnedReferenceSet = [];

/// <inheritdoc cref="s_analyzerToPinnedReferenceSet"/>
private static readonly ConditionalWeakTable<ISourceGenerator, IsolatedAnalyzerReferenceSet> s_generatorToPinnedReferenceSet = [];

/// <summary>
/// We keep a strong reference here. As long as this <see cref="IsolatedAnalyzerFileReference"/> is passed out and
/// held onto (say by a Project instance), it should keep the IsolatedAssemblyReferenceSet (and its ALC) alive.
/// </summary>
private readonly IsolatedAnalyzerReferenceSet _isolatedAnalyzerReferenceSet = isolatedAnalyzerReferenceSet;

/// <summary>
/// The actual real <see cref="AnalyzerReference"/> we defer our operations to.
/// </summary>
public readonly AnalyzerFileReference UnderlyingAnalyzerFileReference = underlyingAnalyzerReference;

public override string Display => UnderlyingAnalyzerFileReference.Display;
public override string? FullPath => UnderlyingAnalyzerFileReference.FullPath;
public override object Id => UnderlyingAnalyzerFileReference.Id;

public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzers(string language)
=> PinAnalyzers(static (reference, language) => reference.GetAnalyzers(language), language);

public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzersForAllLanguages()
=> PinAnalyzers(static (reference, _) => reference.GetAnalyzersForAllLanguages(), default(VoidResult));

[Obsolete]
public override ImmutableArray<ISourceGenerator> GetGenerators()
=> PinGenerators(static (reference, _) => reference.GetGenerators(), default(VoidResult));

public override ImmutableArray<ISourceGenerator> GetGenerators(string language)
=> PinGenerators(static (reference, language) => reference.GetGenerators(language), language);

public override ImmutableArray<ISourceGenerator> GetGeneratorsForAllLanguages()
=> PinGenerators(static (reference, _) => reference.GetGeneratorsForAllLanguages(), default(VoidResult));

private ImmutableArray<DiagnosticAnalyzer> PinAnalyzers<TArg>(Func<AnalyzerReference, TArg, ImmutableArray<DiagnosticAnalyzer>> getItems, TArg arg)
=> PinItems(s_analyzerToPinnedReferenceSet, getItems, arg);

private ImmutableArray<ISourceGenerator> PinGenerators<TArg>(Func<AnalyzerReference, TArg, ImmutableArray<ISourceGenerator>> getItems, TArg arg)
=> PinItems(s_generatorToPinnedReferenceSet, getItems, arg);

private ImmutableArray<TItem> PinItems<TItem, TArg>(
ConditionalWeakTable<TItem, IsolatedAnalyzerReferenceSet> table,
Func<AnalyzerReference, TArg, ImmutableArray<TItem>> getItems,
TArg arg)
where TItem : class
{
// Keep a reference from each generator to the IsolatedAssemblyReferenceSet. This will ensure it (and the ALC
// it points at) stays alive as long as the generator instance stays alive.
var items = getItems(this.UnderlyingAnalyzerFileReference, arg);

foreach (var item in items)
table.TryAdd(item, _isolatedAnalyzerReferenceSet);

// Note: we want to keep ourselves alive during this call so that neither we nor our reference set get GC'ed
// while we're computing the items.
GC.KeepAlive(this);

return items;
}

public override bool Equals(object? obj)
=> ReferenceEquals(this, obj);

public override int GetHashCode()
=> RuntimeHelpers.GetHashCode(this);

public override string ToString()
=> $"{nameof(IsolatedAnalyzerFileReference)}({UnderlyingAnalyzerFileReference})";
}

#endif
Loading
Loading