Skip to content

Commit 3e53da6

Browse files
committed
WIP
1 parent 453c711 commit 3e53da6

File tree

4 files changed

+91
-69
lines changed

4 files changed

+91
-69
lines changed

src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/HostDiagnosticAnalyzerProvider.cs

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,17 @@
88
using Microsoft.CodeAnalysis.Workspaces.ProjectSystem;
99

1010
namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace;
11-
internal sealed class HostDiagnosticAnalyzerProvider : IHostDiagnosticAnalyzerProvider
12-
{
13-
14-
private readonly ImmutableArray<(AnalyzerFileReference reference, string extensionId)> _analyzerReferences;
1511

16-
public HostDiagnosticAnalyzerProvider(string? razorSourceGenerator)
17-
{
18-
if (razorSourceGenerator == null || !File.Exists(razorSourceGenerator))
19-
{
20-
_analyzerReferences = [];
21-
}
22-
else
23-
{
24-
_analyzerReferences = [(
25-
new AnalyzerFileReference(razorSourceGenerator, new SimpleAnalyzerAssemblyLoader()),
26-
ProjectSystemProject.RazorVsixExtensionId
27-
)];
28-
}
29-
}
30-
31-
public ImmutableArray<(AnalyzerFileReference reference, string extensionId)> GetAnalyzerReferencesInExtensions()
32-
{
33-
return _analyzerReferences;
34-
}
12+
internal sealed class HostDiagnosticAnalyzerProvider(string? razorSourceGenerator) : IHostDiagnosticAnalyzerProvider
13+
{
14+
public ImmutableArray<(AnalyzerFileReference reference, string extensionId)> GetAnalyzerReferencesInExtensions() => [];
3515

36-
private sealed class SimpleAnalyzerAssemblyLoader : IAnalyzerAssemblyLoader
16+
public ImmutableArray<string> GetContentInExtension(string extensionId, string contentTypeName)
3717
{
38-
public void AddDependencyLocation(string fullPath)
39-
{
40-
// This method is used to add a path that should be probed for analyzer dependencies.
41-
// In this simple implementation, we do nothing.
42-
}
43-
44-
public Assembly LoadFromPath(string fullPath)
18+
if (razorSourceGenerator is not null && extensionId == ProjectSystemProject.RazorVsixExtensionId && contentTypeName == ProjectSystemProject.RazorContentTypeName)
4519
{
46-
// This method is used to load an analyzer assembly from the specified path.
47-
// In this simple implementation, we use Assembly.LoadFrom to load the assembly.
48-
return Assembly.LoadFrom(fullPath);
20+
return [razorSourceGenerator];
4921
}
22+
return [];
5023
}
5124
}

src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.cs

Lines changed: 78 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Collections.Immutable;
8+
using System.Linq;
89
using System.Reflection;
910
using Microsoft.CodeAnalysis;
1011
using Microsoft.CodeAnalysis.Diagnostics;
@@ -46,6 +47,31 @@ internal VisualStudioDiagnosticAnalyzerProvider(object extensionManager, Type ty
4647
public ImmutableArray<(AnalyzerFileReference reference, string extensionId)> GetAnalyzerReferencesInExtensions()
4748
=> _lazyAnalyzerReferences.Value;
4849

50+
public ImmutableArray<string> GetContentInExtension(string extensionId, string contentTypeName)
51+
{
52+
try
53+
{
54+
var enabledExtensions = GetEnabledExtensions(contentTypeName);
55+
var extension = enabledExtensions.FirstOrDefault(e => string.Equals(GetExtensionIdentifier(e), extensionId, StringComparison.InvariantCultureIgnoreCase));
56+
if (extension is null)
57+
{
58+
return [];
59+
}
60+
61+
return GetExtensionContent(extension, contentTypeName);
62+
}
63+
catch (TargetInvocationException ex) when (ex.InnerException is InvalidOperationException)
64+
{
65+
// this can be called from any thread, and extension manager could be disposed in the middle of us using it since
66+
// now all these are free-threaded and there is no central coordinator, or API or state is immutable that prevent states from
67+
// changing in the middle of others using it.
68+
//
69+
// fortunately, this only happens on disposing at shutdown, so we just catch the exception and silently swallow it.
70+
// we are about to shutdown anyway.
71+
return [];
72+
}
73+
}
74+
4975
private ImmutableArray<(AnalyzerFileReference reference, string extensionId)> GetAnalyzerReferencesImpl()
5076
{
5177
try
@@ -54,37 +80,12 @@ internal VisualStudioDiagnosticAnalyzerProvider(object extensionManager, Type ty
5480
// implementation of the public interface in internal type. so we can't use dynamic here
5581
var _ = PooledDictionary<AnalyzerFileReference, string>.GetInstance(out var analyzePaths);
5682

57-
// var enabledExtensions = extensionManager.GetEnabledExtensions(AnalyzerContentTypeName);
58-
var extensionManagerType = _extensionManager.GetType();
59-
var extensionManager_GetEnabledExtensionsMethod = extensionManagerType.GetRuntimeMethod("GetEnabledExtensions", [typeof(string)]);
60-
var enabledExtensions = (IEnumerable<object>)extensionManager_GetEnabledExtensionsMethod.Invoke(_extensionManager, [AnalyzerContentTypeName]);
61-
83+
var enabledExtensions = GetEnabledExtensions(AnalyzerContentTypeName);
6284
foreach (var extension in enabledExtensions)
6385
{
64-
var extensionType = extension.GetType();
65-
var extensionType_HeaderProperty = extensionType.GetRuntimeProperty("Header");
66-
var extension_Header = extensionType_HeaderProperty.GetValue(extension);
67-
var extension_HeaderType = extension_Header.GetType();
68-
var extension_HeaderType_Identifier = extension_HeaderType.GetRuntimeProperty("Identifier");
69-
var identifier = (string)extension_HeaderType_Identifier.GetValue(extension_Header);
70-
71-
var extensionType_ContentProperty = extensionType.GetRuntimeProperty("Content");
72-
var extension_Content = (IEnumerable<object>)extensionType_ContentProperty.GetValue(extension);
73-
74-
foreach (var content in extension_Content)
86+
var identifier = GetExtensionIdentifier(extension);
87+
foreach (var assemblyPath in GetExtensionContent(extension, AnalyzerContentTypeName))
7588
{
76-
if (!ShouldInclude(content))
77-
{
78-
continue;
79-
}
80-
81-
var extensionType_GetContentMethod = extensionType.GetRuntimeMethod("GetContentLocation", [_typeIExtensionContent]);
82-
if (extensionType_GetContentMethod?.Invoke(extension, [content]) is not string assemblyPath ||
83-
string.IsNullOrEmpty(assemblyPath))
84-
{
85-
continue;
86-
}
87-
8889
analyzePaths.Add(new AnalyzerFileReference(assemblyPath, AnalyzerAssemblyLoader), identifier);
8990
}
9091
}
@@ -108,13 +109,60 @@ internal VisualStudioDiagnosticAnalyzerProvider(object extensionManager, Type ty
108109
}
109110
}
110111

111-
private static bool ShouldInclude(object content)
112+
private IEnumerable<object> GetEnabledExtensions(string contentTypeName)
113+
{
114+
// var enabledExtensions = extensionManager.GetEnabledExtensions(contentTypeName);
115+
var extensionManagerType = _extensionManager.GetType();
116+
var extensionManager_GetEnabledExtensionsMethod = extensionManagerType.GetRuntimeMethod("GetEnabledExtensions", [typeof(string)]);
117+
var enabledExtensions = (IEnumerable<object>)extensionManager_GetEnabledExtensionsMethod.Invoke(_extensionManager, [contentTypeName]);
118+
return enabledExtensions;
119+
}
120+
121+
private static string GetExtensionIdentifier(object extension)
122+
{
123+
var extensionType = extension.GetType();
124+
var extensionType_HeaderProperty = extensionType.GetRuntimeProperty("Header");
125+
var extension_Header = extensionType_HeaderProperty.GetValue(extension);
126+
var extension_HeaderType = extension_Header.GetType();
127+
var extension_HeaderType_Identifier = extension_HeaderType.GetRuntimeProperty("Identifier");
128+
var identifier = (string)extension_HeaderType_Identifier.GetValue(extension_Header);
129+
return identifier;
130+
}
131+
132+
private ImmutableArray<string> GetExtensionContent(object extension, string contentType)
133+
{
134+
var paths = ArrayBuilder<string>.GetInstance();
135+
var extensionType = extension.GetType();
136+
137+
var extensionType_ContentProperty = extensionType.GetRuntimeProperty("Content");
138+
var extension_Content = (IEnumerable<object>)extensionType_ContentProperty.GetValue(extension);
139+
140+
foreach (var content in extension_Content)
141+
{
142+
if (!ShouldInclude(content, contentType))
143+
{
144+
continue;
145+
}
146+
147+
var extensionType_GetContentMethod = extensionType.GetRuntimeMethod("GetContentLocation", [_typeIExtensionContent]);
148+
if (extensionType_GetContentMethod?.Invoke(extension, [content]) is not string assemblyPath ||
149+
string.IsNullOrEmpty(assemblyPath))
150+
{
151+
continue;
152+
}
153+
paths.Add(assemblyPath);
154+
}
155+
156+
return paths.ToImmutableAndFree();
157+
}
158+
159+
private static bool ShouldInclude(object content, string contentTypeName)
112160
{
113161
// var content_ContentTypeName = content.ContentTypeName;
114162
var contentType = content.GetType();
115163
var contentType_ContentTypeNameProperty = contentType.GetRuntimeProperty("ContentTypeName");
116164
var content_ContentTypeName = contentType_ContentTypeNameProperty.GetValue(content) as string;
117165

118-
return string.Equals(content_ContentTypeName, AnalyzerContentTypeName, StringComparison.InvariantCultureIgnoreCase);
166+
return string.Equals(content_ContentTypeName, contentTypeName, StringComparison.InvariantCultureIgnoreCase);
119167
}
120168
}

src/Workspaces/Core/Portable/Workspace/ProjectSystem/IHostDiagnosticAnalyzerProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ namespace Microsoft.CodeAnalysis.Workspaces.ProjectSystem;
1414
internal interface IHostDiagnosticAnalyzerProvider
1515
{
1616
ImmutableArray<(AnalyzerFileReference reference, string extensionId)> GetAnalyzerReferencesInExtensions();
17+
18+
ImmutableArray<string> GetContentInExtension(string extensionId, string contentTypeName);
1719
}

src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,7 @@ private OneOrMany<string> GetMappedAnalyzerPaths(string fullPath)
11761176
};
11771177

11781178
internal const string RazorVsixExtensionId = "Microsoft.VisualStudio.RazorExtension";
1179+
internal const string RazorContentTypeName = "Microsoft.VisualStudio.RazorCompilerAssembly";
11791180
private static readonly string s_razorSourceGeneratorSdkDirectory = CreateDirectoryPathFragment("Sdks", "Microsoft.NET.Sdk.Razor", "source-generators");
11801181
private static readonly ImmutableArray<string> s_razorSourceGeneratorAssemblyNames =
11811182
[
@@ -1190,16 +1191,14 @@ private OneOrMany<string> GetMappedAnalyzerPaths(string fullPath)
11901191

11911192
private OneOrMany<string> GetMappedRazorSourceGenerator(string fullPath)
11921193
{
1193-
var vsixRazorAnalyzers = _hostInfo.HostDiagnosticAnalyzerProvider.GetAnalyzerReferencesInExtensions().SelectAsArray(
1194-
predicate: item => item.extensionId == RazorVsixExtensionId,
1195-
selector: item => item.reference.FullPath);
1194+
var vsixRazorAssemblies = _hostInfo.HostDiagnosticAnalyzerProvider.GetContentInExtension(RazorVsixExtensionId, RazorContentTypeName);
11961195

1197-
if (!vsixRazorAnalyzers.IsEmpty)
1196+
if (!vsixRazorAssemblies.IsEmpty)
11981197
{
11991198
if (s_razorSourceGeneratorAssemblyRootedFileNames.Any(
12001199
static (fileName, fullPath) => fullPath.EndsWith(fileName, StringComparison.OrdinalIgnoreCase), fullPath))
12011200
{
1202-
return OneOrMany.Create(vsixRazorAnalyzers);
1201+
return OneOrMany.Create(vsixRazorAssemblies);
12031202
}
12041203

12051204
return OneOrMany<string>.Empty;

0 commit comments

Comments
 (0)