-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Make Razor redirect code more robust #78039
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,10 +5,13 @@ | |||||
Imports System.ComponentModel.Composition | ||||||
Imports System.IO | ||||||
Imports System.Threading | ||||||
Imports Microsoft.Build.Construction | ||||||
Imports Microsoft.CodeAnalysis | ||||||
Imports Microsoft.CodeAnalysis.Diagnostics | ||||||
Imports Microsoft.CodeAnalysis.Host.Mef | ||||||
Imports Microsoft.CodeAnalysis.Test.Utilities | ||||||
Imports Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting | ||||||
Imports Microsoft.CodeAnalysis.Workspaces.ProjectSystem | ||||||
Imports Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics | ||||||
Imports Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics | ||||||
Imports Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework | ||||||
|
@@ -17,6 +20,26 @@ Imports Roslyn.Test.Utilities | |||||
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim | ||||||
<[UseExportProvider]> | ||||||
Public Class AnalyzerReferenceTests | ||||||
|
||||||
Private Shared Function CreateAnalyzerDir(ParamArray fileNames As String()) As String | ||||||
Dim dir = Path.Combine(TempRoot.Root, Guid.NewGuid().ToString()) | ||||||
Directory.CreateDirectory(dir) | ||||||
For Each fileName In fileNames | ||||||
Dim filePath = Path.Combine(dir, fileName) | ||||||
File.WriteAllBytes(filePath, {}) | ||||||
Next | ||||||
Return dir | ||||||
End Function | ||||||
|
||||||
Private Shared Function GetAnalyzerFilePaths(analyzers As IEnumerable(Of AnalyzerReference)) As List(Of String) | ||||||
Dim list = New List(Of String) | ||||||
For Each a In analyzers | ||||||
Dim filePath = DirectCast(a, AnalyzerFileReference).FullPath | ||||||
list.Add(filePath) | ||||||
Next | ||||||
Return list | ||||||
End Function | ||||||
|
||||||
<WpfFact> | ||||||
Public Async Function RemoveAndReAddInSameBatchWorksCorrectly() As Task | ||||||
Using environment = New TestEnvironment(GetType(TestDynamicFileInfoProviderThatProducesNoFiles)) | ||||||
|
@@ -99,84 +122,195 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim | |||||
End Function | ||||||
|
||||||
<WpfFact> | ||||||
Public Async Function RazorSourceGenerator_FromVsix() As Task | ||||||
Public Async Function RazorSourceGenerator_FromVsix_VsixHasMoreDlls() As Task | ||||||
Using environment = New TestEnvironment() | ||||||
|
||||||
Dim vsixDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Analyzer.dll") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't Util.dll be listed here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should really get refactored so the test setup is one helper for all tests to avoid this oops. |
||||||
|
||||||
Dim providerFactory = DirectCast(environment.ExportProvider.GetExportedValue(Of IVisualStudioDiagnosticAnalyzerProviderFactory), MockVisualStudioDiagnosticAnalyzerProviderFactory) | ||||||
providerFactory.Extensions = | ||||||
{ | ||||||
({ | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll"), | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "VsixDependency1.dll"), | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "VsixDependency2.dll") | ||||||
Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName), | ||||||
Path.Combine(vsixDir, "Analyzer.dll"), | ||||||
Path.Combine(vsixDir, "Util.dll") | ||||||
}, | ||||||
"Microsoft.VisualStudio.RazorExtension"), | ||||||
({ | ||||||
Path.Combine(TempRoot.Root, "File.dll") | ||||||
}, | ||||||
"AnotherExtension") | ||||||
ProjectSystemProject.RazorVsixExtensionId) | ||||||
} | ||||||
|
||||||
Dim project = Await environment.ProjectFactory.CreateAndAddToWorkspaceAsync( | ||||||
"Project", LanguageNames.CSharp, CancellationToken.None) | ||||||
|
||||||
' adding just Razor dependency and not the main source generator is a no-op | ||||||
project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "SdkDependency1.dll")) | ||||||
Dim getAnalyzerRefs = | ||||||
Function() As IReadOnlyList(Of AnalyzerReference) | ||||||
Return environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences | ||||||
End Function | ||||||
Comment on lines
+144
to
+147
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just inline this logic into GetAnalyzerFilePaths since it's being repeated everywhere. |
||||||
|
||||||
Assert.Empty(environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences) | ||||||
' Deliberately left Util.dll as we want to verify the behavior when the sdk and vsix disagree on total | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
' set of dependencies | ||||||
Dim sdkDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Analyzer.dll") | ||||||
|
||||||
' removing just Razor dependency and not the main source generator is a no-op | ||||||
project.RemoveAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "SdkDependency1.dll")) | ||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, "Analyzer.dll")) | ||||||
Assert.Empty(getAnalyzerRefs()) | ||||||
|
||||||
Assert.Empty(environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences) | ||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
Assert.Equal(3, getAnalyzerRefs().Count) | ||||||
Assert.Equal( | ||||||
{ | ||||||
Path.Combine(vsixDir, "Analyzer.dll"), | ||||||
Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName), | ||||||
Path.Combine(vsixDir, "Util.dll") | ||||||
}, GetAnalyzerFilePaths(getAnalyzerRefs()).OrderBy(Function(x) Path.GetFileName(x), StringComparer.Ordinal)) | ||||||
|
||||||
' add Razor source generator and a couple more other analyzer files: | ||||||
project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "SdkDependency1.dll")) | ||||||
project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll")) | ||||||
project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Some other directory", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll")) | ||||||
project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Dir", "File.dll")) | ||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, "Analyzer.dll")) | ||||||
Assert.Equal(3, getAnalyzerRefs().Count) | ||||||
|
||||||
AssertEx.Equal( | ||||||
{ | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll"), | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "VsixDependency1.dll"), | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "VsixDependency2.dll"), | ||||||
Path.Combine(TempRoot.Root, "Some other directory", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll"), | ||||||
Path.Combine(TempRoot.Root, "Dir", "File.dll") | ||||||
}, environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences.Select(Function(r) r.FullPath)) | ||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
Assert.Empty(getAnalyzerRefs()) | ||||||
End Using | ||||||
End Function | ||||||
|
||||||
' add Razor source generator again: | ||||||
Assert.Throws(Of ArgumentException)( | ||||||
"fullPath", | ||||||
Sub() project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll"))) | ||||||
<WpfFact> | ||||||
Public Async Function RazorSourceGenerator_FromVsix_SdkHasMoreDlls() As Task | ||||||
Using environment = New TestEnvironment() | ||||||
|
||||||
AssertEx.Equal( | ||||||
Dim vsixDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Analyzer.dll") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be Util.dll to match the vsix setup below? Applies to the tests below as well. |
||||||
|
||||||
Dim providerFactory = DirectCast(environment.ExportProvider.GetExportedValue(Of IVisualStudioDiagnosticAnalyzerProviderFactory), MockVisualStudioDiagnosticAnalyzerProviderFactory) | ||||||
providerFactory.Extensions = | ||||||
{ | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll"), | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "VsixDependency1.dll"), | ||||||
Path.Combine(TempRoot.Root, "RazorVsix", "VsixDependency2.dll"), | ||||||
Path.Combine(TempRoot.Root, "Some other directory", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll"), | ||||||
Path.Combine(TempRoot.Root, "Dir", "File.dll") | ||||||
}, environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences.Select(Function(r) r.FullPath)) | ||||||
({ | ||||||
Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName), | ||||||
Path.Combine(vsixDir, "Util.dll") | ||||||
}, | ||||||
ProjectSystemProject.RazorVsixExtensionId) | ||||||
} | ||||||
Comment on lines
+179
to
+187
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider extracting this to a helper. |
||||||
|
||||||
' remove: | ||||||
project.RemoveAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "SdkDependency1.dll")) | ||||||
project.RemoveAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll")) | ||||||
project.RemoveAnalyzerReference(Path.Combine(TempRoot.Root, "Some other directory", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll")) | ||||||
Dim project = Await environment.ProjectFactory.CreateAndAddToWorkspaceAsync( | ||||||
"Project", LanguageNames.CSharp, CancellationToken.None) | ||||||
|
||||||
AssertEx.Equal( | ||||||
Dim getAnalyzerRefs = | ||||||
Function() As IReadOnlyList(Of AnalyzerReference) | ||||||
Return environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences | ||||||
End Function | ||||||
|
||||||
Dim sdkDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Analyzer.dll", "Util.dll") | ||||||
|
||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, "Analyzer.dll")) | ||||||
Assert.Empty(getAnalyzerRefs()) | ||||||
|
||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, "Util.dll")) | ||||||
Assert.Empty(getAnalyzerRefs()) | ||||||
|
||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
Assert.Equal(2, getAnalyzerRefs().Count) | ||||||
Assert.Equal( | ||||||
{ | ||||||
Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName), | ||||||
Path.Combine(vsixDir, "Util.dll") | ||||||
}, GetAnalyzerFilePaths(getAnalyzerRefs()).OrderBy(Function(x) Path.GetFileName(x), StringComparer.Ordinal)) | ||||||
Comment on lines
+206
to
+211
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding just a "assert the file names are" helper; then you could just use that one helper everywhere, and also replace all the count assertions with that which makes it a bit clearer you expect things didn't change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I'm also not a fan of the count assertion before the set assertion: if it's got not-2, it'll fail, but the failure assertion won't make it obvious what happened.) |
||||||
|
||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, "Analyzer.dll")) | ||||||
Assert.Equal(2, getAnalyzerRefs().Count) | ||||||
|
||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, "Util.dll")) | ||||||
Assert.Equal(2, getAnalyzerRefs().Count) | ||||||
|
||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
Assert.Empty(getAnalyzerRefs()) | ||||||
End Using | ||||||
End Function | ||||||
|
||||||
<WpfFact> | ||||||
Public Async Function RazorSourceGenerator_FromVsix_OtherGenerators() As Task | ||||||
Using environment = New TestEnvironment() | ||||||
|
||||||
Dim vsixDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Analyzer.dll") | ||||||
|
||||||
Dim providerFactory = DirectCast(environment.ExportProvider.GetExportedValue(Of IVisualStudioDiagnosticAnalyzerProviderFactory), MockVisualStudioDiagnosticAnalyzerProviderFactory) | ||||||
providerFactory.Extensions = | ||||||
{ | ||||||
Path.Combine(TempRoot.Root, "Dir", "File.dll") | ||||||
}, environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences.Select(Function(r) r.FullPath)) | ||||||
({ | ||||||
Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName), | ||||||
Path.Combine(vsixDir, "Util.dll") | ||||||
}, | ||||||
ProjectSystemProject.RazorVsixExtensionId) | ||||||
} | ||||||
|
||||||
' remove again: | ||||||
Assert.Throws(Of ArgumentException)( | ||||||
"fullPath", | ||||||
Sub() project.RemoveAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll"))) | ||||||
Dim project = Await environment.ProjectFactory.CreateAndAddToWorkspaceAsync( | ||||||
"Project", LanguageNames.CSharp, CancellationToken.None) | ||||||
|
||||||
AssertEx.Equal( | ||||||
Dim getAnalyzerRefs = | ||||||
Function() As IReadOnlyList(Of AnalyzerReference) | ||||||
Return environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences | ||||||
End Function | ||||||
|
||||||
Dim sdkDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Util.dll") | ||||||
Dim otherDir = CreateAnalyzerDir("SpellChecker.dll") | ||||||
|
||||||
project.AddAnalyzerReference(Path.Combine(otherDir, "SpellChecker.dll")) | ||||||
Assert.Equal(1, getAnalyzerRefs().Count) | ||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
Assert.Equal(3, getAnalyzerRefs().Count) | ||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, "Util.dll")) | ||||||
Assert.Equal(3, getAnalyzerRefs().Count) | ||||||
|
||||||
Assert.Equal( | ||||||
{ | ||||||
Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName), | ||||||
Path.Combine(otherDir, "SpellChecker.dll"), | ||||||
Path.Combine(vsixDir, "Util.dll") | ||||||
}, GetAnalyzerFilePaths(getAnalyzerRefs()).OrderBy(Function(x) Path.GetFileName(x), StringComparer.Ordinal)) | ||||||
|
||||||
project.RemoveAnalyzerReference(Path.Combine(otherDir, "SpellChecker.dll")) | ||||||
Assert.Equal(2, getAnalyzerRefs().Count) | ||||||
project.RemoveAnalyzerReference(Path.Combine(vsixDir, "Util.dll")) | ||||||
Assert.Equal(2, getAnalyzerRefs().Count) | ||||||
project.RemoveAnalyzerReference(Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
Assert.Empty(getAnalyzerRefs()) | ||||||
End Using | ||||||
End Function | ||||||
|
||||||
<WpfFact> | ||||||
Public Async Function RazorSourceGenerator_FromVsix_ErrorCases() As Task | ||||||
Using environment = New TestEnvironment() | ||||||
|
||||||
Dim vsixDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Analyzer.dll") | ||||||
|
||||||
Dim providerFactory = DirectCast(environment.ExportProvider.GetExportedValue(Of IVisualStudioDiagnosticAnalyzerProviderFactory), MockVisualStudioDiagnosticAnalyzerProviderFactory) | ||||||
providerFactory.Extensions = | ||||||
{ | ||||||
Path.Combine(TempRoot.Root, "Dir", "File.dll") | ||||||
}, environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences.Select(Function(r) r.FullPath)) | ||||||
({ | ||||||
Path.Combine(vsixDir, ProjectSystemProject.RazorSourceGenaratorDllName), | ||||||
Path.Combine(vsixDir, "Util.dll") | ||||||
}, | ||||||
ProjectSystemProject.RazorVsixExtensionId) | ||||||
} | ||||||
|
||||||
Dim project = Await environment.ProjectFactory.CreateAndAddToWorkspaceAsync( | ||||||
"Project", LanguageNames.CSharp, CancellationToken.None) | ||||||
|
||||||
Dim getAnalyzerRefs = | ||||||
Function() As IReadOnlyList(Of AnalyzerReference) | ||||||
Return environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences | ||||||
End Function | ||||||
|
||||||
Dim sdkDir = CreateAnalyzerDir(ProjectSystemProject.RazorSourceGenaratorDllName, "Util.dll") | ||||||
|
||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
project.AddAnalyzerReference(Path.Combine(sdkDir, "Util.dll")) | ||||||
|
||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, "Util.dll")) | ||||||
|
||||||
' This does not throw because within the razor generator only the actual generator dll triggers real | ||||||
' add / removes. At a glance this may seem problematic but it's an unavoidable outcome once the | ||||||
' project system begins mapping / mangling analyzer paths. There will be cases where existing names | ||||||
' do not map to analyzers and it ends up with this behavior. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be fixed by putting a bit more logic into I could be convinced to go the other direction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with this, since at some point I hope this gets properly refactored. 😄 |
||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, "Util.dll")) | ||||||
|
||||||
project.RemoveAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName)) | ||||||
Assert.Throws(Of ArgumentException)( | ||||||
Sub() project.RemoveAnalyzerReference(Path.Combine(sdkDir, ProjectSystemProject.RazorSourceGenaratorDllName))) | ||||||
End Using | ||||||
End Function | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider moving the OrderBy that's used by all callers here