diff --git a/.vscode/launch.json b/.vscode/launch.json index ae371f1da31ef..5c446623b4d14 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,10 @@ // If you have changed target frameworks, make sure to update the program path. "program": "${workspaceFolder}/artifacts/bin/BuildValidator/Debug/netcoreapp3.1/BuildValidator.dll", "args": [ - "--assembliesPath", "./artifacts/obj/RunTests", + "--assembliesPath", "./artifacts/obj/csc/Debug/netcoreapp3.1", + "--referencesPath", "./artifacts/bin", + "--referencesPath", "C:/Program Files/dotnet/packs/Microsoft.AspNetCore.App.Ref", + "--referencesPath", "C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref", "--debugPath", "./artifacts/BuildValidator", "--sourcePath", "." ], diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bb28e7fecfffc..13b78bf4cffba 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -215,7 +215,8 @@ jobs: - job: Correctness_Rebuild pool: - vmImage: windows-2019 + name: NetCorePublic-Pool + queue: BuildPool.Windows.10.Amd64.Open timeoutInMinutes: 90 steps: - template: eng/pipelines/checkout-windows-task.yml @@ -226,13 +227,7 @@ jobs: filePath: eng/build.ps1 arguments: -configuration Debug -prepareMachine -ci -restore -binaryLog - - task: PowerShell@2 - displayName: Build - inputs: - filePath: eng/build.ps1 - arguments: -configuration Debug -prepareMachine -ci -build -bootstrap -publish -binaryLog -skipDocumentation - - - script: .\artifacts\bin\BuildValidator\Debug\net472\BuildValidator.exe --assembliesPath .\artifacts\obj\Microsoft.CodeAnalysis --debugPath .\artifacts\BuildValidator --sourcePath . + - powershell: .\eng\test-rebuild.ps1 -ci displayName: Run BuildValidator - task: PublishBuildArtifacts@1 diff --git a/eng/Versions.props b/eng/Versions.props index ff47291de76c7..d9cf03240810c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -91,9 +91,8 @@ 16.9.0-beta1.21055.5 1.5.0 3.0.0-preview1-03617-02 - 2.1.1 - 2.1.1 - 2.1.1 + 5.0.0 + 5.0.0 3.13.8 15.8.27812-alpha 14.3.25407-alpha diff --git a/eng/build-utils.ps1 b/eng/build-utils.ps1 index 6e32af8866751..3ba5c48b83003 100644 --- a/eng/build-utils.ps1 +++ b/eng/build-utils.ps1 @@ -333,7 +333,7 @@ function Make-BootstrapBuild([switch]$force32 = $false) { Run-MSBuild $projectPath "/restore /t:Pack /p:RoslynEnforceCodeStyle=false /p:RunAnalyzersDuringBuild=false /p:DotNetUseShippingVersions=true /p:InitialDefineConstants=BOOTSTRAP /p:PackageOutputPath=`"$dir`" /p:EnableNgenOptimization=false /p:PublishWindowsPdb=false $force32Flag" -logFileName "Bootstrap" -configuration $bootstrapConfiguration -runAnalyzers $packageFile = Get-ChildItem -Path $dir -Filter "$packageName.*.nupkg" - Unzip (Join-Path $dir $packageFile) $dir + Unzip (Join-Path $dir $packageFile.Name) $dir Write-Host "Cleaning Bootstrap compiler artifacts" Run-MSBuild $projectPath "/t:Clean" -logFileName "BootstrapClean" diff --git a/eng/test-rebuild.ps1 b/eng/test-rebuild.ps1 index 10b83f7ce4a73..3bb1aac9eb35e 100644 --- a/eng/test-rebuild.ps1 +++ b/eng/test-rebuild.ps1 @@ -6,6 +6,7 @@ param( [string]$configuration = "Debug", [switch]$ci = $false, + [switch]$noBuild = $false, [switch]$help) Set-StrictMode -version 2.0 @@ -15,6 +16,7 @@ function Print-Usage() { Write-Host "Usage: test-rebuild.ps1" Write-Host " -configuration Build configuration ('Debug' or 'Release')" Write-Host " -ci Set when running on CI server" + Write-Host " -noBuild If set, skips running a bootstrap build before running the rebuild" Write-Host " -help Print help and exit" } @@ -27,9 +29,21 @@ try { . (Join-Path $PSScriptRoot "build-utils.ps1") Push-Location $RepoRoot - Write-Host "Building Roslyn" - Exec-Console (Join-Path $PSScriptRoot "build.ps1") "-restore -build -ci:$ci -configuration:$configuration -pack -binaryLog" - Exec-Console "artifacts\bin\BuildValidator\$configuration\net472\BuildValidator.exe" "--assembliesPath '$ArtifactsDir/obj/Microsoft.CodeAnalysis'" + if (-not $noBuild) { + Write-Host "Building Roslyn" + Exec-Block { & (Join-Path $PSScriptRoot "build.ps1") -build -bootstrap -ci:$ci -configuration:$configuration -pack -binaryLog } + } + + $dotnetInstallDir = (InitializeDotNetCli -install:$true) + $rebuildArgs = ("--verbose" + + " --assembliesPath `"$ArtifactsDir/obj/Microsoft.CodeAnalysis/$configuration`"" + + " --assembliesPath $ArtifactsDir/obj/csc/$configuration/netcoreapp3.1" + + " --debugPath `"$ArtifactsDir/BuildValidator`"" + + " --sourcePath `"$RepoRoot`"" + + " --referencesPath `"$ArtifactsDir/bin`"" + + " --referencesPath `"$dotnetInstallDir/packs/Microsoft.AspNetCore.App.Ref`"" + + " --referencesPath `"$dotnetInstallDir/packs/Microsoft.NETCore.App.Ref`"") + Exec-Console "$ArtifactsDir/bin/BuildValidator/$configuration/net472/BuildValidator.exe" $rebuildArgs exit 0 } diff --git a/src/Compilers/CSharp/csc/csc.csproj b/src/Compilers/CSharp/csc/csc.csproj index 01e6741bb0961..cc6e6cabf8098 100644 --- a/src/Compilers/CSharp/csc/csc.csproj +++ b/src/Compilers/CSharp/csc/csc.csproj @@ -12,6 +12,7 @@ true false true + true diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs index cfb3d808f9c38..3ca7347fd4759 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs @@ -34,13 +34,16 @@ public class ExtensionMethodImportCompletionProviderTests : AbstractCSharpComple private bool HideAdvancedMembers { get; set; } + private bool UsePartialSemantic { get; set; } = false; + protected override OptionSet WithChangedOptions(OptionSet options) { return options .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, ShowImportCompletionItemsOptionValue) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) .WithChangedOption(CompletionOptions.HideAdvancedMembers, LanguageNames.CSharp, HideAdvancedMembers) - .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, TimeoutInMilliseconds); + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, TimeoutInMilliseconds) + .WithChangedOption(CompletionServiceOptions.UsePartialSemanticForImportCompletion, UsePartialSemantic); } protected override TestComposition GetComposition() diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs index 808c679e06f5c..61cf9343de22a 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs @@ -33,13 +33,16 @@ internal override Type GetCompletionProviderType() private bool HideAdvancedMembers { get; set; } + private bool UsePartialSemantic { get; set; } = false; + protected override OptionSet WithChangedOptions(OptionSet options) { return options .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, ShowImportCompletionItemsOptionValue) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) .WithChangedOption(CompletionServiceOptions.DisallowAddingImports, DisallowAddingImports) - .WithChangedOption(CompletionOptions.HideAdvancedMembers, LanguageNames.CSharp, HideAdvancedMembers); + .WithChangedOption(CompletionOptions.HideAdvancedMembers, LanguageNames.CSharp, HideAdvancedMembers) + .WithChangedOption(CompletionServiceOptions.UsePartialSemanticForImportCompletion, UsePartialSemantic); } protected override TestComposition GetComposition() diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs index 0b561853b1862..6002a22d63a53 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs @@ -349,9 +349,14 @@ public Task WriteStreamAsync(Document document, string name, Stream stream return SpecializedTasks.True; } - public virtual void Dispose() + public void Dispose() { } + + public ValueTask DisposeAsync() + { + return ValueTaskFactory.CompletedTask; + } } } } diff --git a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs index 5d7b4aef25381..4db4b72a73af6 100644 --- a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs @@ -127,7 +127,7 @@ public async Task TestPreviewServices() var persistentService = previewWorkspace.Services.GetRequiredService(); - using var storage = await persistentService.GetStorageAsync(previewWorkspace.CurrentSolution, CancellationToken.None); + await using var storage = await persistentService.GetStorageAsync(previewWorkspace.CurrentSolution, CancellationToken.None); Assert.IsType(storage); } diff --git a/src/EditorFeatures/Text/Extensions.cs b/src/EditorFeatures/Text/Extensions.cs index c38c3d7ab7c61..c0abe4ace4d07 100644 --- a/src/EditorFeatures/Text/Extensions.cs +++ b/src/EditorFeatures/Text/Extensions.cs @@ -84,19 +84,6 @@ public static IEnumerable GetRelatedDocumentsWithChanges(this ITextSna public static IEnumerable GetRelatedDocuments(this ITextBuffer buffer) => buffer.AsTextContainer().GetRelatedDocuments(); - /// - /// Tries to get the document corresponding to the text from the current partial solution - /// associated with the text's container. If the document does not contain the exact text a document - /// from a new solution containing the specified text is constructed. If no document is associated - /// with the specified text's container, or the text's container isn't associated with a workspace, - /// then the method returns false. - /// - internal static Document? GetDocumentWithFrozenPartialSemantics(this SourceText text, CancellationToken cancellationToken) - { - var document = text.GetOpenDocumentInCurrentContextWithChanges(); - return document?.WithFrozenPartialSemantics(cancellationToken); - } - internal static bool CanApplyChangeDocumentToWorkspace(this ITextBuffer buffer) => Workspace.TryGetWorkspace(buffer.AsTextContainer(), out var workspace) && workspace.CanApplyChange(ApplyChangesKind.ChangeDocument); diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb index c9cf512757760..93a087195ce35 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb @@ -18,12 +18,14 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Private Property TimeoutInMilliseconds As Integer = -1 Private Property ShowImportCompletionItemsOptionValue As Boolean = True + Private Property UsePartialSemantic As Boolean = False Protected Overrides Function WithChangedOptions(options As OptionSet) As OptionSet Return options _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue) _ .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) _ - .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, TimeoutInMilliseconds) + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, TimeoutInMilliseconds) _ + .WithChangedOption(CompletionServiceOptions.UsePartialSemanticForImportCompletion, UsePartialSemantic) End Function Protected Overrides Function GetComposition() As TestComposition diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.vb index 890b260696255..a21a9f8bafc6a 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.vb @@ -15,11 +15,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Private Property ShowImportCompletionItemsOptionValue As Boolean = True Private Property IsExpandedCompletion As Boolean = True + Private Property UsePartialSemantic As Boolean = False Protected Overrides Function WithChangedOptions(options As OptionSet) As OptionSet Return options _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue) _ - .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) + .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) _ + .WithChangedOption(CompletionServiceOptions.UsePartialSemanticForImportCompletion, UsePartialSemantic) End Function Protected Overrides Function GetComposition() As TestComposition diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ExtensionMethodImportCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ExtensionMethodImportCompletionProvider.cs index 02e8cc228aba9..da74b72eff123 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ExtensionMethodImportCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ExtensionMethodImportCompletionProvider.cs @@ -44,8 +44,8 @@ protected override ImmutableArray GetImportedNamespaces( CancellationToken cancellationToken) => ImportCompletionProviderHelper.GetImportedNamespaces(location, semanticModel); - protected override Task CreateContextAsync(Document document, int position, CancellationToken cancellationToken) - => ImportCompletionProviderHelper.CreateContextAsync(document, position, cancellationToken); + protected override Task CreateContextAsync(Document document, int position, bool usePartialSemantic, CancellationToken cancellationToken) + => ImportCompletionProviderHelper.CreateContextAsync(document, position, usePartialSemantic, cancellationToken); protected override bool IsFinalSemicolonOfUsingOrExtern(SyntaxNode directive, SyntaxToken token) { diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ImportCompletionProviderHelper.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ImportCompletionProviderHelper.cs index 10208430c8253..5115b26714e29 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ImportCompletionProviderHelper.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/ImportCompletionProviderHelper.cs @@ -21,11 +21,15 @@ public static ImmutableArray GetImportedNamespaces( => semanticModel.GetUsingNamespacesInScope(location) .SelectAsArray(namespaceSymbol => namespaceSymbol.ToDisplayString(SymbolDisplayFormats.NameFormat)); - public static async Task CreateContextAsync(Document document, int position, CancellationToken cancellationToken) + public static async Task CreateContextAsync(Document document, int position, bool usePartialSemantic, CancellationToken cancellationToken) { // Need regular semantic model because we will use it to get imported namespace symbols. Otherwise we will try to // reach outside of the span and ended up with "node not within syntax tree" error from the speculative model. - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + // Also we use partial model unless full model is explictly request (e.g. in tests) so that we don't have to wait for all semantics to be computed. + var semanticModel = usePartialSemantic + ? await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(false) + : await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + Contract.ThrowIfNull(semanticModel); return CSharpSyntaxContext.CreateContext(document.Project.Solution.Workspace, semanticModel, position, cancellationToken); } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/TypeImportCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/TypeImportCompletionProvider.cs index 6d7dd6cc3a0e4..59e6118414cbd 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/TypeImportCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ImportCompletion/TypeImportCompletionProvider.cs @@ -45,8 +45,8 @@ protected override ImmutableArray GetImportedNamespaces( CancellationToken cancellationToken) => ImportCompletionProviderHelper.GetImportedNamespaces(location, semanticModel); - protected override Task CreateContextAsync(Document document, int position, CancellationToken cancellationToken) - => ImportCompletionProviderHelper.CreateContextAsync(document, position, cancellationToken); + protected override Task CreateContextAsync(Document document, int position, bool usePartialSemantic, CancellationToken cancellationToken) + => ImportCompletionProviderHelper.CreateContextAsync(document, position, usePartialSemantic, cancellationToken); protected override bool IsFinalSemicolonOfUsingOrExtern(SyntaxNode directive, SyntaxToken token) { diff --git a/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs b/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs index e059d2fb688c3..15df57e3e42a4 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs @@ -14,6 +14,12 @@ internal static class CompletionServiceOptions public static readonly Option2 IsExpandedCompletion = new(nameof(CompletionServiceOptions), nameof(IsExpandedCompletion), defaultValue: false); + /// + /// For testing only. Changing the default value in actual product might cause perf issues. + /// + public static readonly Option2 UsePartialSemanticForImportCompletion + = new(nameof(CompletionServiceOptions), nameof(UsePartialSemanticForImportCompletion), defaultValue: true); + /// /// Indicates if the completion should be disallowed to add imports. /// diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs index 22cce31897d64..b5f68b5edb547 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis.Completion.Providers { internal abstract class AbstractImportCompletionProvider : LSPCompletionProvider { - protected abstract Task CreateContextAsync(Document document, int position, CancellationToken cancellationToken); + protected abstract Task CreateContextAsync(Document document, int position, bool usePartialSemantic, CancellationToken cancellationToken); protected abstract ImmutableArray GetImportedNamespaces(SyntaxNode location, SemanticModel semanticModel, CancellationToken cancellationToken); protected abstract bool ShouldProvideCompletion(CompletionContext completionContext, SyntaxContext syntaxContext); protected abstract Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, HashSet namespacesInScope, bool isExpandedCompletion, CancellationToken cancellationToken); @@ -54,7 +54,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC // We need to check for context before option values, so we can tell completion service that we are in a context to provide expanded items // even though import completion might be disabled. This would show the expander in completion list which user can then use to explicitly ask for unimported items. - var syntaxContext = await CreateContextAsync(document, completionContext.Position, cancellationToken).ConfigureAwait(false); + var usePartialSemantic = completionContext.Options.GetOption(CompletionServiceOptions.UsePartialSemanticForImportCompletion); + var syntaxContext = await CreateContextAsync(document, completionContext.Position, usePartialSemantic, cancellationToken).ConfigureAwait(false); if (!ShouldProvideCompletion(completionContext, syntaxContext)) { return; diff --git a/src/Features/Core/Portable/SolutionCrawler/State/AbstractAnalyzerState.cs b/src/Features/Core/Portable/SolutionCrawler/State/AbstractAnalyzerState.cs index 119913a91aa92..7d3043672cb77 100644 --- a/src/Features/Core/Portable/SolutionCrawler/State/AbstractAnalyzerState.cs +++ b/src/Features/Core/Portable/SolutionCrawler/State/AbstractAnalyzerState.cs @@ -61,7 +61,8 @@ public async Task TryGetExistingDataAsync(TValue value, CancellationToken try { - using var storage = await persistService.GetStorageAsync(solution, cancellationToken).ConfigureAwait(false); + var storage = await persistService.GetStorageAsync(solution, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); using var stream = await ReadStreamAsync(storage, value, cancellationToken).ConfigureAwait(false); if (stream != null) @@ -102,7 +103,8 @@ private async Task WriteToStreamAsync(TValue value, TData data, Cancellati var solution = GetSolution(value); var persistService = solution.Workspace.Services.GetService(); - using var storage = await persistService.GetStorageAsync(solution, cancellationToken).ConfigureAwait(false); + var storage = await persistService.GetStorageAsync(solution, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); stream.Position = 0; return await WriteStreamAsync(storage, value, stream, cancellationToken).ConfigureAwait(false); } diff --git a/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs b/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs index 714219d32c531..68a960905b158 100644 --- a/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs +++ b/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs @@ -116,7 +116,8 @@ private async Task CreateSpellCheckCodeIssueAsync( var originalOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var options = originalOptions .WithChangedOption(CompletionOptions.SnippetsBehavior, document.Project.Language, SnippetsRule.NeverInclude) - .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, document.Project.Language, false); + .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, document.Project.Language, false) + .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, false); var completionList = await service.GetCompletionsAsync( document, nameToken.SpanStart, options: options, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs index 99e8c3d6ab9b7..68cdb23096e87 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs @@ -61,7 +61,7 @@ void M() var solution = testLspServer.TestWorkspace.CurrentSolution; // Make sure the unimported types option is on by default. - solution = solution.WithOptions(solution.Options + testLspServer.TestWorkspace.SetOptions(testLspServer.TestWorkspace.CurrentSolution.Options .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, true) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, true)); diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb index 532159840d425..ff0e435bcafcc 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb @@ -37,8 +37,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Public Overrides ReadOnly Property TriggerCharacters As ImmutableHashSet(Of Char) = CompletionUtilities.CommonTriggerCharsAndParen - Protected Overrides Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) - Return ImportCompletionProviderHelper.CreateContextAsync(document, position, cancellationToken) + Protected Overrides Function CreateContextAsync(document As Document, position As Integer, usePartialSemantic As Boolean, cancellationToken As CancellationToken) As Task(Of SyntaxContext) + Return ImportCompletionProviderHelper.CreateContextAsync(document, position, usePartialSemantic, cancellationToken) End Function Protected Overrides Function GetImportedNamespaces(location As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As ImmutableArray(Of String) diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ImportCompletionProviderHelper.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ImportCompletionProviderHelper.vb index 9c6703fa937cd..8de0fc9964150 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ImportCompletionProviderHelper.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ImportCompletionProviderHelper.vb @@ -30,10 +30,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Return builder.ToImmutableAndFree() End Function - Public Shared Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) + Public Shared Async Function CreateContextAsync(document As Document, position As Integer, usePartialSemantic As Boolean, cancellationToken As CancellationToken) As Task(Of SyntaxContext) ' Need regular semantic model because we will use it to get imported namespace symbols. Otherwise we will try to ' reach outside of the span And ended up with "node not within syntax tree" error from the speculative model. - Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False) + ' Also we use partial model unless full model is explictly request (e.g. in tests) so that we don't have to wait for all semantics to be computed. + Dim semanticModel = If(usePartialSemantic, + Await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(False), + Await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(False)) + Contract.ThrowIfNull(semanticModel) Return VisualBasicSyntaxContext.CreateContext(document.Project.Solution.Workspace, semanticModel, position, cancellationToken) End Function End Class diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb index 5e3cf4798c8c4..70b9147188e26 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb @@ -32,8 +32,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Public Overrides ReadOnly Property TriggerCharacters As ImmutableHashSet(Of Char) = CompletionUtilities.CommonTriggerCharsAndParen - Protected Overrides Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) - Return ImportCompletionProviderHelper.CreateContextAsync(document, position, cancellationToken) + Protected Overrides Function CreateContextAsync(document As Document, position As Integer, usePartialSemantic As Boolean, cancellationToken As CancellationToken) As Task(Of SyntaxContext) + Return ImportCompletionProviderHelper.CreateContextAsync(document, position, usePartialSemantic, cancellationToken) End Function Protected Overrides Function GetImportedNamespaces(location As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As ImmutableArray(Of String) diff --git a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs index 9a86808f42d82..59a27b0e8093e 100644 --- a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs +++ b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs @@ -55,7 +55,7 @@ public async Task RunAsync(CancellationToken cancellationToken) if (usePersistentStorage) { var persistentStorageService = _workspace.Services.GetRequiredService(); - using var persistentStorage = await persistentStorageService.GetStorageAsync(_workspace.CurrentSolution, cancellationToken).ConfigureAwait(false); + await using var persistentStorage = await persistentStorageService.GetStorageAsync(_workspace.CurrentSolution, cancellationToken).ConfigureAwait(false); if (persistentStorage is NoOpPersistentStorage) { throw new InvalidOperationException("Benchmark is not configured to use persistent storage."); diff --git a/src/Tools/BuildValidator/BuildConstructor.cs b/src/Tools/BuildValidator/BuildConstructor.cs index 7919972107383..bfac2b9d93830 100644 --- a/src/Tools/BuildValidator/BuildConstructor.cs +++ b/src/Tools/BuildValidator/BuildConstructor.cs @@ -38,7 +38,7 @@ public BuildConstructor(LocalReferenceResolver referenceResolver, LocalSourceRes _logger = logger; } - public Compilation CreateCompilation(CompilationOptionsReader compilationOptionsReader, string name) + public Compilation CreateCompilation(CompilationOptionsReader compilationOptionsReader, string fileName) { var pdbCompilationOptions = compilationOptionsReader.GetMetadataCompilationOptions(); if (pdbCompilationOptions.Length == 0) @@ -62,8 +62,8 @@ public Compilation CreateCompilation(CompilationOptionsReader compilationOptions { var compilation = language switch { - LanguageNames.CSharp => CreateCSharpCompilation(name, compilationOptionsReader, sources, metadataReferences), - LanguageNames.VisualBasic => CreateVisualBasicCompilation(name, compilationOptionsReader, sources, metadataReferences), + LanguageNames.CSharp => CreateCSharpCompilation(fileName, compilationOptionsReader, sources, metadataReferences), + LanguageNames.VisualBasic => CreateVisualBasicCompilation(fileName, compilationOptionsReader, sources, metadataReferences), _ => throw new InvalidDataException($"{language} is not a known language") }; @@ -88,7 +88,10 @@ void logResolvedSources() { var sourceFileInfo = resolvedSource.SourceFileInfo; var hash = BitConverter.ToString(sourceFileInfo.Hash).Replace("-", ""); - _logger.LogInformation($@"""{resolvedSource.DisplayPath}"" - {sourceFileInfo.HashAlgorithm} - {hash}"); + var embeddedCompressedHash = sourceFileInfo.EmbeddedCompressedHash is { } compressedHash + ? ("[uncompressed]" + BitConverter.ToString(compressedHash).Replace("-", "")) + : null; + _logger.LogInformation($@"""{resolvedSource.DisplayPath}"" - {sourceFileInfo.HashAlgorithm} - {hash} - {embeddedCompressedHash}"); } } } @@ -129,20 +132,20 @@ private ImmutableArray ResolveSources( #region CSharp private Compilation CreateCSharpCompilation( - string assemblyName, + string fileName, CompilationOptionsReader optionsReader, ImmutableArray sources, ImmutableArray metadataReferences) { - var (compilationOptions, parseOptions) = CreateCSharpCompilationOptions(optionsReader, assemblyName); + var (compilationOptions, parseOptions) = CreateCSharpCompilationOptions(optionsReader, fileName); return CSharpCompilation.Create( - assemblyName, + Path.GetFileNameWithoutExtension(fileName), syntaxTrees: sources.Select(s => CSharpSyntaxTree.ParseText(s.SourceText, options: parseOptions, path: s.SourceFileInfo.SourceFilePath)).ToImmutableArray(), references: metadataReferences, options: compilationOptions); } - private (CSharpCompilationOptions, CSharpParseOptions) CreateCSharpCompilationOptions(CompilationOptionsReader optionsReader, string assemblyName) + private (CSharpCompilationOptions, CSharpParseOptions) CreateCSharpCompilationOptions(CompilationOptionsReader optionsReader, string fileName) { using var scope = _logger.BeginScope("Options"); var pdbCompilationOptions = optionsReader.GetMetadataCompilationOptions(); @@ -175,9 +178,7 @@ private Compilation CreateCSharpCompilation( optionsReader.GetOutputKind(), reportSuppressedDiagnostics: false, - // TODO: can't rely on the implicity moduleName here. In the case of .NET Core EXE the output name will - // end with .dll but the inferred name will be .exe - moduleName: assemblyName + ".dll", + moduleName: fileName, mainTypeName: optionsReader.GetMainTypeName(), scriptClassName: null, usings: null, @@ -213,10 +214,10 @@ private Compilation CreateCSharpCompilation( return (compilationOptions, parseOptions); } - private static (OptimizationLevel, bool) GetOptimizationLevel(string optimizationLevel) + private static (OptimizationLevel, bool) GetOptimizationLevel(string? optimizationLevel) => optimizationLevel switch { - "debug" => (OptimizationLevel.Debug, false), + null or "debug" => (OptimizationLevel.Debug, false), "debug-plus" => (OptimizationLevel.Debug, true), "release" => (OptimizationLevel.Release, false), _ => throw new InvalidDataException($"Optimization \"{optimizationLevel}\" level not recognized") @@ -226,14 +227,14 @@ private static (OptimizationLevel, bool) GetOptimizationLevel(string optimizatio #region Visual Basic private Compilation CreateVisualBasicCompilation( - string assemblyName, + string fileName, CompilationOptionsReader optionsReader, ImmutableArray sources, ImmutableArray metadataReferences) { var compilationOptions = CreateVisualBasicCompilationOptions(optionsReader); return VisualBasicCompilation.Create( - assemblyName, + Path.GetFileNameWithoutExtension(fileName), syntaxTrees: sources.Select(s => VisualBasicSyntaxTree.ParseText(s.SourceText, options: compilationOptions.ParseOptions, path: s.DisplayPath)).ToImmutableArray(), references: metadataReferences, options: compilationOptions); @@ -244,7 +245,7 @@ private static VisualBasicCompilationOptions CreateVisualBasicCompilationOptions var pdbCompilationOptions = optionsReader.GetMetadataCompilationOptions(); var langVersionString = pdbCompilationOptions.GetUniqueOption("language-version"); - var optimization = pdbCompilationOptions.GetUniqueOption("optimization"); + pdbCompilationOptions.TryGetUniqueOption("optimization", out var optimization); pdbCompilationOptions.TryGetUniqueOption("define", out var define); pdbCompilationOptions.TryGetUniqueOption("strict", out var strict); pdbCompilationOptions.TryGetUniqueOption("checked", out var checkedString); diff --git a/src/Tools/BuildValidator/BuildValidator.csproj b/src/Tools/BuildValidator/BuildValidator.csproj index 1634c165010d1..1f87b9e58f7e8 100644 --- a/src/Tools/BuildValidator/BuildValidator.csproj +++ b/src/Tools/BuildValidator/BuildValidator.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Tools/BuildValidator/CompilationDiff.cs b/src/Tools/BuildValidator/CompilationDiff.cs index ee3828d61bfd7..f0729cb069dc8 100644 --- a/src/Tools/BuildValidator/CompilationDiff.cs +++ b/src/Tools/BuildValidator/CompilationDiff.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; +using Microsoft.DiaSymReader.Tools; using Microsoft.Extensions.Logging; using Microsoft.Metadata.Tools; @@ -61,6 +62,13 @@ public static unsafe CompilationDiff Create( iconInIcoFormat: null); var sourceLink = optionsReader.GetSourceLinkUTF8(); + + var embeddedTexts = producedCompilation.SyntaxTrees + .Select(st => (path: st.FilePath, text: st.GetText())) + .Where(pair => pair.text.CanBeEmbedded) + .Select(pair => EmbeddedText.FromSource(pair.path, pair.text)) + .ToImmutableArray(); + var emitResult = producedCompilation.Emit( peStream: rebuildPeStream, pdbStream: null, @@ -73,10 +81,7 @@ public static unsafe CompilationDiff Create( metadataPEStream: null, pdbOptionsBlobReader: optionsReader.GetMetadataCompilationOptionsBlobReader(), sourceLinkStream: sourceLink != null ? new MemoryStream(sourceLink) : null, - embeddedTexts: producedCompilation.SyntaxTrees - .Select(st => (path: st.FilePath, text: st.GetText())) - .Where(pair => pair.text.CanBeEmbedded) - .Select(pair => EmbeddedText.FromSource(pair.path, pair.text)), + embeddedTexts: embeddedTexts, cancellationToken: CancellationToken.None); if (!emitResult.Success) @@ -141,6 +146,26 @@ public static unsafe CompilationDiff Create( writeVisualization(originalPeMdvPath, optionsReader.PeReader.GetMetadataReader()); writeVisualization(originalPdbMdvPath, optionsReader.PdbReader); + var originalPdbXmlPath = Path.Combine(originalPath, assemblyName + ".pdb.xml"); + using var originalPdbXml = File.Create(originalPdbXmlPath); + + var rebuildPdbXmlPath = Path.Combine(rebuildPath, assemblyName + ".pdb.xml"); + + var pdbToXmlOptions = PdbToXmlOptions.ResolveTokens + | PdbToXmlOptions.ThrowOnError + | PdbToXmlOptions.ExcludeScopes + | PdbToXmlOptions.IncludeSourceServerInformation + | PdbToXmlOptions.IncludeEmbeddedSources + | PdbToXmlOptions.IncludeTokens + | PdbToXmlOptions.IncludeMethodSpans; + + PdbToXmlConverter.ToXml( + new StreamWriter(originalPdbXml), + pdbStream: new UnmanagedMemoryStream(optionsReader.PdbReader.MetadataPointer, optionsReader.PdbReader.MetadataLength), + peStream: new MemoryStream(originalBytes), + options: pdbToXmlOptions, + methodName: null); + var rebuildPeMdvPath = Path.Combine(rebuildPath, assemblyName + ".pe.mdv"); var rebuildPdbMdvPath = Path.Combine(rebuildPath, assemblyName + ".pdb.mdv"); fixed (byte* ptr = rebuildBytes) @@ -156,6 +181,28 @@ public static unsafe CompilationDiff Create( { var rebuildPdbReader = provider.GetMetadataReader(MetadataReaderOptions.Default); writeVisualization(rebuildPdbMdvPath, rebuildPdbReader); + + using var rebuildPdbXml = File.Create(rebuildPdbXmlPath); + PdbToXmlConverter.ToXml( + new StreamWriter(rebuildPdbXml), + pdbStream: new UnmanagedMemoryStream(rebuildPdbReader.MetadataPointer, rebuildPdbReader.MetadataLength), + peStream: new MemoryStream(rebuildBytes), + options: pdbToXmlOptions, + methodName: null); + + using (logger.BeginScope("Rebuild Embedded Texts raw SHAs")) + { + var rebuildReader = new CompilationOptionsReader(logger, rebuildPdbReader, rebuildPeReader); + var rebuildSourceFileInfos = rebuildReader.GetSourceFileInfos(rebuildReader.GetEncoding()); + foreach (var info in rebuildSourceFileInfos) + { + if (info.EmbeddedCompressedHash is { } hash) + { + var hashString = BitConverter.ToString(hash).Replace("-", ""); + logger.LogInformation($@"""{info.SourceFilePath}"" - {hashString}"); + } + } + } } } @@ -163,11 +210,12 @@ public static unsafe CompilationDiff Create( var ildasmRebuildOutputPath = Path.Combine(rebuildPath, assemblyName + ".il"); // TODO: can we bundle ildasm in with the utility? - Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe", $@"{originalBinaryPath.FullName} /out={ildasmOriginalOutputPath}").WaitForExit(); - Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe", $@"{rebuildAssemblyPath} /out={ildasmRebuildOutputPath}").WaitForExit(); + Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe", $@"{originalBinaryPath.FullName} /all /out={ildasmOriginalOutputPath}").WaitForExit(); + Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe", $@"{rebuildAssemblyPath} /all /out={ildasmRebuildOutputPath}").WaitForExit(); File.WriteAllText(Path.Combine(assemblyDebugPath, "compare-pe.mdv.ps1"), $@"code --diff (Join-Path $PSScriptRoot ""{originalPeMdvPath.Substring(assemblyDebugPath.Length)}"") (Join-Path $PSScriptRoot ""{rebuildPeMdvPath.Substring(assemblyDebugPath.Length)}"")"); File.WriteAllText(Path.Combine(assemblyDebugPath, "compare-pdb.mdv.ps1"), $@"code --diff (Join-Path $PSScriptRoot ""{originalPdbMdvPath.Substring(assemblyDebugPath.Length)}"") (Join-Path $PSScriptRoot ""{rebuildPdbMdvPath.Substring(assemblyDebugPath.Length)}"")"); + File.WriteAllText(Path.Combine(assemblyDebugPath, "compare-pdb.xml.ps1"), $@"code --diff (Join-Path $PSScriptRoot ""{originalPdbXmlPath.Substring(assemblyDebugPath.Length)}"") (Join-Path $PSScriptRoot ""{rebuildPdbXmlPath.Substring(assemblyDebugPath.Length)}"")"); File.WriteAllText(Path.Combine(assemblyDebugPath, "compare-il.ps1"), $@"code --diff (Join-Path $PSScriptRoot ""{ildasmOriginalOutputPath.Substring(assemblyDebugPath.Length)}"") (Join-Path $PSScriptRoot ""{ildasmRebuildOutputPath.Substring(assemblyDebugPath.Length)}"")"); } } diff --git a/src/Tools/BuildValidator/CompilationOptionsReader.cs b/src/Tools/BuildValidator/CompilationOptionsReader.cs index 9b9cd0f14776e..0cdf44a87f7f1 100644 --- a/src/Tools/BuildValidator/CompilationOptionsReader.cs +++ b/src/Tools/BuildValidator/CompilationOptionsReader.cs @@ -27,17 +27,20 @@ internal readonly struct SourceFileInfo internal SourceHashAlgorithm HashAlgorithm { get; } internal byte[] Hash { get; } internal SourceText? EmbeddedText { get; } + internal byte[]? EmbeddedCompressedHash { get; } internal SourceFileInfo( string sourceFilePath, SourceHashAlgorithm hashAlgorithm, byte[] hash, - SourceText? embeddedText) + SourceText? embeddedText, + byte[]? embeddedCompressedHash) { SourceFilePath = sourceFilePath; HashAlgorithm = hashAlgorithm; Hash = hash; EmbeddedText = embeddedText; + EmbeddedCompressedHash = embeddedCompressedHash; } } @@ -192,7 +195,7 @@ public OutputKind GetOutputKind() => return (typeName, methodName); } - private SourceText? ResolveEmbeddedSource(DocumentHandle document, SourceHashAlgorithm hashAlgorithm, Encoding encoding) + private (SourceText? embeddedText, byte[]? compressedHash) ResolveEmbeddedSource(DocumentHandle document, SourceHashAlgorithm hashAlgorithm, Encoding encoding) { byte[] bytes = (from handle in PdbReader.GetCustomDebugInformation(document) let cdi = PdbReader.GetCustomDebugInformation(handle) @@ -201,14 +204,18 @@ where PdbReader.GetGuid(cdi.Kind) == EmbeddedSourceGuid if (bytes == null) { - return null; + return default; } int uncompressedSize = BitConverter.ToInt32(bytes, 0); var stream = new MemoryStream(bytes, sizeof(int), bytes.Length - sizeof(int)); + byte[]? compressedHash = null; if (uncompressedSize != 0) { + using var algorithm = CryptographicHashProvider.TryGetAlgorithm(hashAlgorithm) ?? throw new InvalidOperationException(); + compressedHash = algorithm.ComputeHash(bytes); + var decompressed = new MemoryStream(uncompressedSize); using (var deflater = new DeflateStream(stream, CompressionMode.Decompress)) @@ -227,7 +234,8 @@ where PdbReader.GetGuid(cdi.Kind) == EmbeddedSourceGuid using (stream) { // todo: IVT and EncodedStringText.Create? - return SourceText.From(stream, encoding: encoding, checksumAlgorithm: hashAlgorithm, canBeEmbedded: true); + var embeddedText = SourceText.From(stream, encoding: encoding, checksumAlgorithm: hashAlgorithm, canBeEmbedded: true); + return (embeddedText, compressedHash); } } @@ -293,7 +301,7 @@ public ImmutableArray GetSourceFileInfos(Encoding encoding) var hash = PdbReader.GetBlobBytes(document.Hash); var embeddedContent = ResolveEmbeddedSource(documentHandle, hashAlgorithm, encoding); - builder.Add(new SourceFileInfo(name, hashAlgorithm, hash, embeddedContent)); + builder.Add(new SourceFileInfo(name, hashAlgorithm, hash, embeddedContent.embeddedText, embeddedContent.compressedHash)); } return builder.MoveToImmutable(); diff --git a/src/Tools/BuildValidator/LocalReferenceResolver.cs b/src/Tools/BuildValidator/LocalReferenceResolver.cs index 72de32d852b86..242260162265a 100644 --- a/src/Tools/BuildValidator/LocalReferenceResolver.cs +++ b/src/Tools/BuildValidator/LocalReferenceResolver.cs @@ -30,11 +30,10 @@ internal class LocalReferenceResolver public LocalReferenceResolver(Options options, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); - foreach (var directoryInfo in GetRefAssembliesDirectories()) + foreach (var path in options.AssembliesPaths) { - _indexDirectories.Add(directoryInfo); + _indexDirectories.Add(new DirectoryInfo(path)); } - _indexDirectories.Add(new DirectoryInfo(options.AssembliesPath)); _indexDirectories.Add(GetNugetCacheDirectory()); foreach (var path in options.ReferencesPaths) { @@ -44,7 +43,7 @@ public LocalReferenceResolver(Options options, ILoggerFactory loggerFactory) using var _ = _logger.BeginScope("Assembly Reference Search Paths"); foreach (var directory in _indexDirectories) { - _logger.LogInformation($@"""{directory}"""); + _logger.LogInformation($@"""{directory.FullName}"""); } } @@ -59,16 +58,6 @@ public static DirectoryInfo GetNugetCacheDirectory() return new DirectoryInfo(nugetPackageDirectory); } - public static DirectoryInfo[] GetRefAssembliesDirectories() - { - // TODO: Don't hardcode the paths here. - return new[] - { - new DirectoryInfo(@"C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref"), - new DirectoryInfo(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref") - }; - } - public string GetReferencePath(MetadataReferenceInfo referenceInfo) { if (_cache.TryGetValue(referenceInfo.Mvid, out var value)) diff --git a/src/Tools/BuildValidator/Options.cs b/src/Tools/BuildValidator/Options.cs index b7a23f5e1c982..31f269ff34fc7 100644 --- a/src/Tools/BuildValidator/Options.cs +++ b/src/Tools/BuildValidator/Options.cs @@ -10,7 +10,7 @@ namespace BuildValidator { internal record Options( - string AssembliesPath, + string[] AssembliesPaths, string[] ReferencesPaths, string SourcePath, bool Verbose, diff --git a/src/Tools/BuildValidator/Program.cs b/src/Tools/BuildValidator/Program.cs index 941eea0f318b1..734921b08fc0a 100644 --- a/src/Tools/BuildValidator/Program.cs +++ b/src/Tools/BuildValidator/Program.cs @@ -43,14 +43,14 @@ static int Main(string[] args) var rootCommand = new RootCommand { new Option( - "--assembliesPath", "Path to assemblies to rebuild" - ) { IsRequired = true }, + "--assembliesPath", "Path to assemblies to rebuild (can be specified one or more times)" + ) { IsRequired = true, Argument = { Arity = ArgumentArity.OneOrMore } }, new Option( "--sourcePath", "Path to sources to use in rebuild" ) { IsRequired = true }, - new Option( - "--referencesPaths", "Additional paths to referenced assemblies" - ), + new Option( + "--referencesPath", "Path to referenced assemblies (can be specified zero or more times)" + ) { Argument = { Arity = ArgumentArity.ZeroOrMore } }, new Option( "--verbose", "Output verbose log information" ), @@ -64,32 +64,30 @@ static int Main(string[] args) "--debugPath", "Path to output debug info. Defaults to the user temp directory. Note that a unique debug path should be specified for every instance of the tool running with `--debug` enabled." ) }; - rootCommand.Handler = CommandHandler.Create(HandleCommand); + rootCommand.Handler = CommandHandler.Create(HandleCommand); return rootCommand.Invoke(args); } - static int HandleCommand(string assembliesPath, string sourcePath, string[]? referencesPaths, bool verbose, bool quiet, bool debug, string? debugPath) + static int HandleCommand(string[] assembliesPath, string sourcePath, string[]? referencesPath, bool verbose, bool quiet, bool debug, string? debugPath) { // If user provided a debug path then assume we should write debug outputs. debug |= debugPath is object; debugPath ??= Path.Combine(Path.GetTempPath(), $"BuildValidator"); - referencesPaths ??= Array.Empty(); + referencesPath ??= Array.Empty(); - var options = new Options(assembliesPath, referencesPaths, sourcePath, verbose, quiet, debug, debugPath); + var options = new Options(assembliesPath, referencesPath, sourcePath, verbose, quiet, debug, debugPath); - // TODO: remove the DemoLoggerProvider, update this dependency, - // and move to the built in logger. - var loggerFactory = new LoggerFactory( - new[] { new ConsoleLoggerProvider(new ConsoleLoggerSettings()) }, - new LoggerFilterOptions() + // TODO: remove the DemoLoggerProvider or convert it to something more permanent + var loggerFactory = LoggerFactory.Create(builder => + { + builder.SetMinimumLevel((options.Verbose, options.Quiet) switch { - MinLevel = options.Verbose ? LogLevel.Trace : LogLevel.Information + (_, true) => LogLevel.Error, + (true, _) => LogLevel.Trace, + _ => LogLevel.Information }); - - if (!options.Quiet) - { - loggerFactory.AddProvider(new DemoLoggerProvider()); - } + builder.AddProvider(new DemoLoggerProvider()); + }); var logger = loggerFactory.CreateLogger(); try @@ -111,10 +109,11 @@ static int HandleCommand(string assembliesPath, string sourcePath, string[]? ref var buildConstructor = new BuildConstructor(referenceResolver, sourceResolver, logger); - var artifactsDir = new DirectoryInfo(options.AssembliesPath); + var artifactsDirs = options.AssembliesPaths.Select(path => new DirectoryInfo(path)); - var filesToValidate = artifactsDir.EnumerateFiles("*.exe", SearchOption.AllDirectories) - .Concat(artifactsDir.EnumerateFiles("*.dll", SearchOption.AllDirectories)) + var filesToValidate = artifactsDirs.SelectMany(dir => + dir.EnumerateFiles("*.exe", SearchOption.AllDirectories) + .Concat(dir.EnumerateFiles("*.dll", SearchOption.AllDirectories))) .Distinct(FileNameEqualityComparer.Instance); var success = ValidateFiles(filesToValidate, buildConstructor, logger, options); @@ -213,7 +212,7 @@ private static bool ValidateFiles(IEnumerable originalBinaries, BuildC var compilation = buildConstructor.CreateCompilation( optionsReader, - Path.GetFileNameWithoutExtension(originalBinary.Name)); + originalBinary.Name); var compilationDiff = CompilationDiff.Create(originalBinary, optionsReader, compilation, getDebugEntryPoint(), logger, options); return compilationDiff; diff --git a/src/Tools/ManifestGenerator/README.md b/src/Tools/ManifestGenerator/README.md index ef0eaaf06037e..d796d86bfb342 100644 --- a/src/Tools/ManifestGenerator/README.md +++ b/src/Tools/ManifestGenerator/README.md @@ -1,4 +1,3 @@ # dotnet-roslyn-manifest-generator -https://github.com/dotnet/roslyn/blob/efd69176d6ad7f9dbe1d87525201760a6c44e7a0/docs/compilers/terrapin.md#artifacts-manifest-file - +[Terrapin doc](../../../docs/compilers/terrapin.md#artifacts-manifest-file) diff --git a/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs b/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs index c7528173a19b1..4c54f7cca1358 100644 --- a/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs +++ b/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs @@ -100,7 +100,7 @@ public async Task TestNullFilePaths() var streamName = "stream"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); var project = solution.Projects.First(); var document = project.Documents.First(); Assert.False(await storage.WriteStreamAsync(project, streamName, EncodeString(""))); @@ -118,13 +118,13 @@ public async Task PersistentService_Solution_WriteReadDifferentInstances(Size si var streamName1 = "PersistentService_Solution_WriteReadDifferentInstances1"; var streamName2 = "PersistentService_Solution_WriteReadDifferentInstances2"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); Assert.True(await storage.WriteStreamAsync(streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum))); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum)))); Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName2, GetChecksum2(withChecksum)))); @@ -139,7 +139,7 @@ public async Task PersistentService_Solution_WriteReadReopenSolution(Size size, var streamName1 = "PersistentService_Solution_WriteReadReopenSolution1"; var streamName2 = "PersistentService_Solution_WriteReadReopenSolution2"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); Assert.True(await storage.WriteStreamAsync(streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum))); @@ -147,7 +147,7 @@ public async Task PersistentService_Solution_WriteReadReopenSolution(Size size, solution = CreateOrOpenSolution(); - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum)))); Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName2, GetChecksum2(withChecksum)))); @@ -162,7 +162,7 @@ public async Task PersistentService_Solution_WriteReadSameInstance(Size size, bo var streamName1 = "PersistentService_Solution_WriteReadSameInstance1"; var streamName2 = "PersistentService_Solution_WriteReadSameInstance2"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); Assert.True(await storage.WriteStreamAsync(streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum))); @@ -178,7 +178,7 @@ public async Task PersistentService_Project_WriteReadSameInstance(Size size, boo var streamName1 = "PersistentService_Project_WriteReadSameInstance1"; var streamName2 = "PersistentService_Project_WriteReadSameInstance2"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); var project = solution.Projects.Single(); Assert.True(await storage.WriteStreamAsync(project, streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); @@ -196,7 +196,7 @@ public async Task PersistentService_Document_WriteReadSameInstance(Size size, bo var streamName1 = "PersistentService_Document_WriteReadSameInstance1"; var streamName2 = "PersistentService_Document_WriteReadSameInstance2"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); var document = solution.Projects.Single().Documents.Single(); Assert.True(await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); @@ -213,7 +213,7 @@ public async Task PersistentService_Solution_SimultaneousWrites() var streamName1 = "PersistentService_Solution_SimultaneousWrites1"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); DoSimultaneousWrites(s => storage.WriteStreamAsync(streamName1, EncodeString(s))); var value = int.Parse(ReadStringToEnd(await storage.ReadStreamAsync(streamName1))); Assert.True(value >= 0); @@ -227,7 +227,7 @@ public async Task PersistentService_Project_SimultaneousWrites() var streamName1 = "PersistentService_Project_SimultaneousWrites1"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); DoSimultaneousWrites(s => storage.WriteStreamAsync(solution.Projects.Single(), streamName1, EncodeString(s))); var value = int.Parse(ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single(), streamName1))); Assert.True(value >= 0); @@ -241,7 +241,7 @@ public async Task PersistentService_Document_SimultaneousWrites() var streamName1 = "PersistentService_Document_SimultaneousWrites1"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); DoSimultaneousWrites(s => storage.WriteStreamAsync(solution.Projects.Single().Documents.Single(), streamName1, EncodeString(s))); var value = int.Parse(ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single().Documents.Single(), streamName1))); Assert.True(value >= 0); @@ -273,7 +273,7 @@ public async Task PersistentService_Solution_SimultaneousReads(Size size, bool w var solution = CreateOrOpenSolution(); var streamName1 = "PersistentService_Solution_SimultaneousReads1"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); DoSimultaneousReads(async () => ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum))), GetData1(size)); } @@ -285,7 +285,7 @@ public async Task PersistentService_Project_SimultaneousReads(Size size, bool wi var solution = CreateOrOpenSolution(); var streamName1 = "PersistentService_Project_SimultaneousReads1"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); Assert.True(await storage.WriteStreamAsync(solution.Projects.Single(), streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); DoSimultaneousReads(async () => ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single(), streamName1, GetChecksum1(withChecksum))), GetData1(size)); } @@ -299,7 +299,7 @@ public async Task PersistentService_Document_SimultaneousReads(Size size, bool w var solution = CreateOrOpenSolution(); var streamName1 = "PersistentService_Document_SimultaneousReads1"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); Assert.True(await storage.WriteStreamAsync(solution.Projects.Single().Documents.Single(), streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum))); DoSimultaneousReads(async () => ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single().Documents.Single(), streamName1, GetChecksum1(withChecksum))), GetData1(size)); } @@ -311,7 +311,7 @@ public async Task TestReadChecksumReturnsNullWhenNeverWritten() var streamName1 = "TestReadChecksumReturnsNullWhenNeverWritten"; - using var storage = await GetStorageAsync(solution); + await using var storage = await GetStorageAsync(solution); Assert.False(await storage.ChecksumMatchesAsync(streamName1, s_checksum1)); } @@ -322,12 +322,12 @@ public async Task TestCanReadWithNullChecksumSomethingWrittenWithNonNullChecksum var streamName1 = "TestCanReadWithNullChecksumSomethingWrittenWithNonNullChecksum"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), s_checksum1)); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, checksum: null))); } @@ -340,12 +340,12 @@ public async Task TestCannotReadWithMismatchedChecksums() var streamName1 = "TestCannotReadWithMismatchedChecksums"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), s_checksum1)); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.Null(await storage.ReadStreamAsync(streamName1, s_checksum2)); } @@ -358,12 +358,12 @@ public async Task TestCannotReadChecksumIfWriteDidNotIncludeChecksum() var streamName1 = "TestCannotReadChecksumIfWriteDidNotIncludeChecksum"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: null)); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.False(await storage.ChecksumMatchesAsync(streamName1, s_checksum1)); } @@ -376,12 +376,12 @@ public async Task TestReadChecksumProducesWrittenChecksum() var streamName1 = "TestReadChecksumProducesWrittenChecksum"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1)); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(streamName1, s_checksum1)); } @@ -394,13 +394,13 @@ public async Task TestReadChecksumProducesLastWrittenChecksum1() var streamName1 = "TestReadChecksumProducesLastWrittenChecksum1"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1)); Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: null)); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.False(await storage.ChecksumMatchesAsync(streamName1, s_checksum1)); } @@ -413,13 +413,13 @@ public async Task TestReadChecksumProducesLastWrittenChecksum2() var streamName1 = "TestReadChecksumProducesLastWrittenChecksum2"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: null)); Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1)); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(streamName1, s_checksum1)); } @@ -432,13 +432,13 @@ public async Task TestReadChecksumProducesLastWrittenChecksum3() var streamName1 = "TestReadChecksumProducesLastWrittenChecksum3"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1)); Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum2)); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(streamName1, s_checksum2)); } @@ -452,12 +452,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKey() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -472,12 +472,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocument() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -492,12 +492,12 @@ public async Task TestOpenWithSolutionReadWithDocumentKey() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -512,12 +512,12 @@ public async Task TestOpenWithSolutionReadWithDocument() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -532,12 +532,12 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument1() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -555,12 +555,12 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument2() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -578,12 +578,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -601,12 +601,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2() var streamName1 = "stream"; - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -624,12 +624,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKey_WriteWithSolutionKe var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -644,12 +644,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocument_WriteWithSolutionKey() var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -664,12 +664,12 @@ public async Task TestOpenWithSolutionReadWithDocumentKey_WriteWithSolutionKey() var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -684,12 +684,12 @@ public async Task TestOpenWithSolutionReadWithDocument_WriteWithSolutionKey() var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -704,12 +704,12 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument1_WriteWithS var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -727,12 +727,12 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument2_WriteWithS var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageAsync(solution)) + await using (var storage = await GetStorageAsync(solution)) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -750,12 +750,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1_WriteWi var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -773,12 +773,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2_WriteWi var streamName1 = "stream"; - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(Size.Small)), checksum: s_checksum1); } - using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) + await using (var storage = await GetStorageFromKeyAsync(solution.Workspace, SolutionKey.ToSolutionKey(solution))) { Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(Size.Small), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); diff --git a/src/VisualStudio/CSharp/Test/PersistentStorage/SQLiteV2PersistentStorageTests.cs b/src/VisualStudio/CSharp/Test/PersistentStorage/SQLiteV2PersistentStorageTests.cs index 5bb184aa6c720..f05948bfd630a 100644 --- a/src/VisualStudio/CSharp/Test/PersistentStorage/SQLiteV2PersistentStorageTests.cs +++ b/src/VisualStudio/CSharp/Test/PersistentStorage/SQLiteV2PersistentStorageTests.cs @@ -40,7 +40,7 @@ public async Task TestCrashInNewConnection() // Because instantiating the connection will fail, we will not get back // a working persistent storage. - using (var storage = await GetStorageAsync(solution, faultInjector)) + await using (var storage = await GetStorageAsync(solution, faultInjector)) using (var memStream = new MemoryStream()) using (var streamWriter = new StreamWriter(memStream)) { diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs index 084d2450d05f4..4b9026b29ed95 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs @@ -49,7 +49,8 @@ public async Task SerializeAsync(IPersistentStorageService persistentServi WriteDiagnosticData(writer, items, cancellationToken); } - using var storage = await persistentService.GetStorageAsync(project.Solution, cancellationToken).ConfigureAwait(false); + var storage = await persistentService.GetStorageAsync(project.Solution, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); stream.Position = 0; @@ -69,7 +70,8 @@ public async ValueTask> DeserializeAsync(IPersist { Contract.ThrowIfFalse(textDocument == null || textDocument.Project == project); - using var storage = await persistentService.GetStorageAsync(project.Solution, cancellationToken).ConfigureAwait(false); + var storage = await persistentService.GetStorageAsync(project.Solution, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); var readTask = (textDocument != null) ? textDocument is Document document ? diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 197b4473abff6..08f90282d197d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -70,55 +70,54 @@ private static async Task TryLoadOrCreateAsync( // Ok, we can use persistence. First try to load from the persistence service. var persistentStorageService = (IChecksummedPersistentStorageService)solution.Workspace.Services.GetService(); - T result; - using (var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false)) + var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); + + // Get the unique key to identify our data. + var key = PrefixMetadataSymbolTreeInfo + keySuffix; + using (var stream = await storage.ReadStreamAsync(key, checksum, cancellationToken).ConfigureAwait(false)) + using (var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken)) { - // Get the unique key to identify our data. - var key = PrefixMetadataSymbolTreeInfo + keySuffix; - using (var stream = await storage.ReadStreamAsync(key, checksum, cancellationToken).ConfigureAwait(false)) - using (var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken)) + if (reader != null) { - if (reader != null) + // We have some previously persisted data. Attempt to read it back. + // If we're able to, and the version of the persisted data matches + // our version, then we can reuse this instance. + var read = tryReadObject(reader); + if (read != null) { - // We have some previously persisted data. Attempt to read it back. - // If we're able to, and the version of the persisted data matches - // our version, then we can reuse this instance. - result = tryReadObject(reader); - if (result != null) - { - // If we were able to read something in, it's checksum better - // have matched the checksum we expected. - Debug.Assert(result.Checksum == checksum); - return result; - } + // If we were able to read something in, it's checksum better + // have matched the checksum we expected. + Debug.Assert(read.Checksum == checksum); + return read; } } + } - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - // Couldn't read from the persistence service. If we've been asked to only load - // data and not create new instances in their absence, then there's nothing left - // to do at this point. - if (loadOnly) - { - return null; - } + // Couldn't read from the persistence service. If we've been asked to only load + // data and not create new instances in their absence, then there's nothing left + // to do at this point. + if (loadOnly) + { + return null; + } - // Now, try to create a new instance and write it to the persistence service. - result = await CreateWithLoggingAsync().ConfigureAwait(false); - Contract.ThrowIfNull(result); + // Now, try to create a new instance and write it to the persistence service. + var result = await CreateWithLoggingAsync().ConfigureAwait(false); + Contract.ThrowIfNull(result); - using (var stream = SerializableBytes.CreateWritableStream()) + using (var stream = SerializableBytes.CreateWritableStream()) + { + using (var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken)) { - using (var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken)) - { - result.WriteTo(writer); - } + result.WriteTo(writer); + } - stream.Position = 0; + stream.Position = 0; - await storage.WriteStreamAsync(key, stream, checksum, cancellationToken).ConfigureAwait(false); - } + await storage.WriteStreamAsync(key, stream, checksum, cancellationToken).ConfigureAwait(false); } return result; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index 73e570290fb4b..518e4a3cdeb7f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -28,7 +28,8 @@ internal sealed partial class SyntaxTreeIndex : IObjectWritable try { // attempt to load from persisted state - using var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); using var stream = await storage.ReadStreamAsync(document, PersistenceName, checksum, cancellationToken).ConfigureAwait(false); using var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken); if (reader != null) @@ -71,7 +72,8 @@ private async Task SaveAsync( try { - using var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); using var stream = SerializableBytes.CreateWritableStream(); using (var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken)) @@ -99,7 +101,8 @@ private static async Task PrecalculatedAsync( // check whether we already have info for this document try { - using var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + var storage = await persistentStorageService.GetStorageAsync(solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); // Check if we've already stored a checksum and it matches the checksum we // expect. If so, we're already precalculated and don't have to recompute // this index. Otherwise if we don't have a checksum, or the checksums don't diff --git a/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs b/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs index b91d57fe0e641..21743014759f0 100644 --- a/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs +++ b/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs @@ -234,6 +234,9 @@ public static IChecksummedPersistentStorage AddReferenceCountToAndCreateWrapper( public void Dispose() => _storage.Dispose(); + public ValueTask DisposeAsync() + => _storage.DisposeAsync(); + public Task ChecksumMatchesAsync(string name, Checksum checksum, CancellationToken cancellationToken) => _storage.Target.ChecksumMatchesAsync(name, checksum, cancellationToken); @@ -249,13 +252,13 @@ public Task ChecksumMatchesAsync(ProjectKey project, string name, Checksum public Task ChecksumMatchesAsync(DocumentKey document, string name, Checksum checksum, CancellationToken cancellationToken) => _storage.Target.ChecksumMatchesAsync(document, name, checksum, cancellationToken); - public Task ReadStreamAsync(string name, CancellationToken cancellationToken) + public Task ReadStreamAsync(string name, CancellationToken cancellationToken) => _storage.Target.ReadStreamAsync(name, cancellationToken); - public Task ReadStreamAsync(Project project, string name, CancellationToken cancellationToken) + public Task ReadStreamAsync(Project project, string name, CancellationToken cancellationToken) => _storage.Target.ReadStreamAsync(project, name, cancellationToken); - public Task ReadStreamAsync(Document document, string name, CancellationToken cancellationToken) + public Task ReadStreamAsync(Document document, string name, CancellationToken cancellationToken) => _storage.Target.ReadStreamAsync(document, name, cancellationToken); public Task ReadStreamAsync(string name, Checksum checksum, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs index 85c0e7dbef7e8..418b29c4b8240 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs @@ -88,6 +88,12 @@ private SQLitePersistentStorage( } public override void Dispose() + { + var task = DisposeAsync().AsTask(); + task.Wait(); + } + + public override async ValueTask DisposeAsync() { try { @@ -95,7 +101,7 @@ public override void Dispose() // persisted to the DB. try { - FlushWritesOnClose(); + await FlushWritesOnCloseAsync().ConfigureAwait(false); } catch (Exception e) { diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs index 58f3a7ee276e3..7bb665bd52a99 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs @@ -31,13 +31,13 @@ private Task FlushInMemoryDataToDiskIfNotShutdownAsync(CancellationToken cancell return PerformWriteAsync(FlushInMemoryDataToDisk, cancellationToken); } - private void FlushWritesOnClose() + private Task FlushWritesOnCloseAsync() { // Issue a write task to write this all out to disk. // // Note: this only happens on close, so we don't try to avoid allocations here. - var writeTask = PerformWriteAsync( + return PerformWriteAsync( () => { // Perform the actual write while having exclusive access to the scheduler. @@ -59,9 +59,6 @@ private void FlushWritesOnClose() // read/write after releasing us. _shutdownTokenSource.Cancel(); }, CancellationToken.None); - - // Wait for that task to finish. - writeTask.Wait(); } private void FlushInMemoryDataToDisk() diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs index 4fc5a526d715e..8fe74c72239b2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs @@ -35,6 +35,7 @@ protected AbstractPersistentStorage( } public abstract void Dispose(); + public abstract ValueTask DisposeAsync(); public abstract Task ChecksumMatchesAsync(string name, Checksum checksum, CancellationToken cancellationToken); public abstract Task ReadStreamAsync(string name, Checksum? checksum, CancellationToken cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorage.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorage.cs index 0c33811f395a5..c15c8d1601937 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorage.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorage.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.IO; using System.Threading; @@ -11,12 +9,16 @@ namespace Microsoft.CodeAnalysis.Host { - public interface IPersistentStorage : IDisposable + /// + /// Instances of support both synchronous and asynchronous disposal. Asynchronous + /// disposal should always be preferred as the implementation of synchronous disposal may end up blocking the caller + /// on async work. + /// + public interface IPersistentStorage : IDisposable, IAsyncDisposable { - Task ReadStreamAsync(string name, CancellationToken cancellationToken = default); - - Task ReadStreamAsync(Project project, string name, CancellationToken cancellationToken = default); - Task ReadStreamAsync(Document document, string name, CancellationToken cancellationToken = default); + Task ReadStreamAsync(string name, CancellationToken cancellationToken = default); + Task ReadStreamAsync(Project project, string name, CancellationToken cancellationToken = default); + Task ReadStreamAsync(Document document, string name, CancellationToken cancellationToken = default); Task WriteStreamAsync(string name, Stream stream, CancellationToken cancellationToken = default); Task WriteStreamAsync(Project project, string name, Stream stream, CancellationToken cancellationToken = default); diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorage.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorage.cs index 030716cb1406c..d2bf20ad513a6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorage.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorage.cs @@ -22,6 +22,11 @@ public void Dispose() { } + public ValueTask DisposeAsync() + { + return ValueTaskFactory.CompletedTask; + } + public Task ChecksumMatchesAsync(string name, Checksum checksum, CancellationToken cancellationToken) => SpecializedTasks.False; diff --git a/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs b/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs index 2c18b2b54f6a4..5b017c4bdf750 100644 --- a/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs +++ b/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Threading; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Text @@ -92,5 +93,18 @@ public static ImmutableArray GetRelatedDocuments(this SourceTextContai return null; } + + /// + /// Tries to get the document corresponding to the text from the current partial solution + /// associated with the text's container. If the document does not contain the exact text a document + /// from a new solution containing the specified text is constructed. If no document is associated + /// with the specified text's container, or the text's container isn't associated with a workspace, + /// then the method returns false. + /// + internal static Document? GetDocumentWithFrozenPartialSemantics(this SourceText text, CancellationToken cancellationToken) + { + var document = text.GetOpenDocumentInCurrentContextWithChanges(); + return document?.WithFrozenPartialSemantics(cancellationToken); + } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassificationCache/RemoteSemanticClassificationCacheService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassificationCache/RemoteSemanticClassificationCacheService.cs index 55d905ebd223d..8ceebb90a08b1 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassificationCache/RemoteSemanticClassificationCacheService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassificationCache/RemoteSemanticClassificationCacheService.cs @@ -94,7 +94,8 @@ private static async Task CacheSemanticClassificationsAsync(Document document, C if (persistenceService == null) return; - using var storage = await persistenceService.GetStorageAsync(solution, cancellationToken).ConfigureAwait(false); + var storage = await persistenceService.GetStorageAsync(solution, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); if (storage == null) return; @@ -264,7 +265,8 @@ private async Task> TryReadCachedSemanticClassifi if (persistenceService == null) return default; - using var storage = await persistenceService.GetStorageAsync(workspace, documentKey.Project.Solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + var storage = await persistenceService.GetStorageAsync(workspace, documentKey.Project.Solution, checkBranchId: false, cancellationToken).ConfigureAwait(false); + await using var _ = storage.ConfigureAwait(false); if (storage == null) return default; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IReferenceCountedDisposable.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IReferenceCountedDisposable.cs index 953989d8e950b..16c85b3680415 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IReferenceCountedDisposable.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IReferenceCountedDisposable.cs @@ -13,6 +13,9 @@ namespace Roslyn.Utilities /// /// internal interface IReferenceCountedDisposable : IDisposable +#if !CODE_STYLE + , IAsyncDisposable +#endif where T : class { /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposable.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposable.cs index 3f950b1f7484f..ccc27196b760c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposable.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposable.cs @@ -4,6 +4,8 @@ using System; using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; namespace Roslyn.Utilities { @@ -173,6 +175,27 @@ private ReferenceCountedDisposable(T instance, StrongBox referenceCount) /// use. /// public void Dispose() + { + var instanceToDispose = DisposeImpl(); + instanceToDispose?.Dispose(); + } + + public ValueTask DisposeAsync() + { + var instanceToDispose = DisposeImpl(); + if (instanceToDispose == null) + return ValueTaskFactory.CompletedTask; + +#if !CODE_STYLE + if (instanceToDispose is IAsyncDisposable asyncDisposable) + return asyncDisposable.DisposeAsync(); +#endif + + instanceToDispose.Dispose(); + return ValueTaskFactory.CompletedTask; + } + + private T? DisposeImpl() { T? instanceToDispose = null; lock (_boxedReferenceCount) @@ -180,7 +203,7 @@ public void Dispose() if (_instance == null) { // Already disposed; allow multiple without error. - return; + return null; } _boxedReferenceCount.Value--; @@ -193,7 +216,7 @@ public void Dispose() _instance = null; } - instanceToDispose?.Dispose(); + return instanceToDispose; } /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs index 87fcdf4704255..cd86b1d66a3e5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; namespace Roslyn.Utilities { @@ -15,8 +16,7 @@ namespace Roslyn.Utilities internal sealed class ReferenceCountedDisposableCache where TValue : class, IDisposable where TKey : notnull { - private readonly Dictionary.WeakReference> _cache = - new(); + private readonly Dictionary.WeakReference> _cache = new(); private readonly object _gate = new(); public IReferenceCountedDisposable> GetOrCreate(TKey key, Func valueCreator, TArg arg)