From 17a7f7080b7d121dbe2d79d51d044b60e44cd532 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 23 Sep 2022 09:31:58 -0700 Subject: [PATCH 01/32] Revert "Revert "Deprecate usage of Workspace in TextLoader (breaking change) (#63616)" (#64228)" This reverts commit 9d2a58ea593d6ebe2843c70d1e5c54e015cc73f5. --- docs/Breaking API Changes.md | 7 +++ eng/config/BannedSymbols.txt | 3 +- .../Core/Interactive/InteractiveSession.cs | 2 +- .../Workspaces/EditorTextFactoryService.cs | 6 +- .../EditAndContinueWorkspaceServiceTests.cs | 22 +++---- .../Test/Workspaces/TextFactoryTests.cs | 3 +- .../Workspaces/TestHostDocument.cs | 2 +- ...compilationMetadataAsSourceFileProvider.cs | 3 +- .../MetadataAsSourceGeneratedFileInfo.cs | 2 +- ...rceDocumentMetadataAsSourceFileProvider.cs | 2 +- .../LspMiscellaneousFilesWorkspace.cs | 4 +- .../Lsif/Generator/CompilerInvocation.cs | 2 +- .../Implementation/AbstractEditorFactory.cs | 4 +- .../PreviewUpdater.PreviewDialogWorkspace.cs | 8 +-- .../MiscellaneousFilesWorkspace.cs | 3 +- ...tudioProject.BatchingDocumentCollection.cs | 6 +- ...sualStudioWorkspaceImpl.OpenFileTracker.cs | 9 ++- .../Projects/RemoteProjectInfoProvider.cs | 24 -------- .../RoslynRemoteProjectInfoProvider.cs | 7 ++- ... => WorkspaceFileTextLoaderNoException.cs} | 10 ++-- .../Client/RemoteLanguageServiceWorkspace.cs | 13 +++-- .../Core/Desktop/PublicAPI.Unshipped.txt | 5 +- .../MSBuild/MSBuildProjectLoader.Worker.cs | 4 +- ...dProjectLoader.Worker_ResolveReferences.cs | 2 +- .../Core/MSBuild/MSBuild/MSBuildWorkspace.cs | 2 +- .../Core/Portable/PublicAPI.Unshipped.txt | 6 ++ .../Portable/Workspace/CommandLineProject.cs | 4 +- .../Core/Portable/Workspace/FileTextLoader.cs | 31 ++++++---- .../Host/TextFactory/ITextFactoryService.cs | 4 +- .../Host/TextFactory/TextFactoryService.cs | 6 +- .../Workspace/Solution/SolutionChanges.cs | 7 +-- .../Workspace/Solution/TextDocumentState.cs | 24 +++----- .../Portable/Workspace/Solution/TextLoader.cs | 57 ++++++++++++------- .../Core/Portable/Workspace/Workspace.cs | 1 + .../Workspace/WorkspaceFileTextLoader.cs | 33 +++++++++++ .../Portable/Workspace/Workspace_Editor.cs | 7 +-- .../SolutionTests/DocumentInfoTests.cs | 5 +- .../CoreTest/SolutionTests/SolutionTests.cs | 23 +++----- .../CoreTestUtilities/TestTextLoader.cs | 2 +- .../VisualStudioMSBuildWorkspaceTests.cs | 4 +- 40 files changed, 208 insertions(+), 161 deletions(-) rename src/VisualStudio/LiveShare/Impl/Client/Projects/{FileTextLoaderNoException.cs => WorkspaceFileTextLoaderNoException.cs} (71%) create mode 100644 src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs diff --git a/docs/Breaking API Changes.md b/docs/Breaking API Changes.md index cfdf98d31c450..72b8a95c02c85 100644 --- a/docs/Breaking API Changes.md +++ b/docs/Breaking API Changes.md @@ -64,3 +64,10 @@ Roslyn does not support implementing completion for arbitrary languages. ### `Microsoft.CodeAnalysis.CodeStyle.NotificationOption` is now immutable All property setters now throw an exception. + +# Version 4.4.0 + +`Workspace.OnWorkspaceFailed` is no longer called when an error occurs while reading source file content from disk. + +The `Workspace` and `DocumentId` parameters of `TextLoader.LoadTextAndVersionAsync(Workspace, DocumentId, CancellationToken)` are deprecated. +The method now receives an instance of an immutable empty `Workspace` with default workspace services, and a fake `DocumentId`. diff --git a/eng/config/BannedSymbols.txt b/eng/config/BannedSymbols.txt index 054e6fb4c538a..c3d693297ea10 100644 --- a/eng/config/BannedSymbols.txt +++ b/eng/config/BannedSymbols.txt @@ -34,4 +34,5 @@ M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAna M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.SyntaxAnnotation,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.Text.TextSpan,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,System.Collections.Generic.IEnumerable{Microsoft.CodeAnalysis.Text.TextSpan},Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions -M:Microsoft.CodeAnalysis.Editing.SyntaxEditor.#ctor(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.Host.HostWorkspaceServices); Use overload that takes HostSolutionServices instead \ No newline at end of file +M:Microsoft.CodeAnalysis.Editing.SyntaxEditor.#ctor(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.Host.HostWorkspaceServices); Use overload that takes HostSolutionServices instead +M:Microsoft.CodeAnalysis.FileTextLoader.#ctor(System.String,System.Text.Encoding); use WorkspaceFileTextLoader \ No newline at end of file diff --git a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs index 49b8f9c01ea07..fe4b91a12eab6 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs @@ -233,7 +233,7 @@ private void AddSubmissionProjectNoLock(ITextBuffer submissionBuffer, string lan solution = initProject.Solution.AddDocument( DocumentId.CreateNewId(initializationScriptProjectId, debugName: initializationScriptPath), Path.GetFileName(initializationScriptPath), - new FileTextLoader(initializationScriptPath, defaultEncoding: null)); + new WorkspaceFileTextLoader(solution.Services, initializationScriptPath, defaultEncoding: null)); } var newSubmissionProject = CreateSubmissionProjectNoLock(solution, _currentSubmissionProjectId, _lastSuccessfulSubmissionProjectId, languageName, imports, references); diff --git a/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs b/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs index 287d60b9f9ead..f3f931c48bbb7 100644 --- a/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs +++ b/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { [ExportWorkspaceService(typeof(ITextFactoryService), ServiceLayer.Editor), Shared] - internal class EditorTextFactoryService : ITextFactoryService + internal sealed class EditorTextFactoryService : ITextFactoryService { private readonly ITextBufferCloneService _textBufferCloneService; private readonly ITextBufferFactoryService _textBufferFactory; @@ -37,7 +37,7 @@ public EditorTextFactoryService( private static readonly Encoding s_throwingUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken = default) + public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken) { // this API is for a case where user wants us to figure out encoding from the given stream. // if defaultEncoding is given, we will use it if we couldn't figure out encoding used in the stream ourselves. @@ -70,7 +70,7 @@ public SourceText CreateText(Stream stream, Encoding? defaultEncoding, Cancellat } } - public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken = default) + public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken) { // this API is for a case where user just wants to create a source text with explicit encoding. var buffer = CreateTextBuffer(reader); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 803c82d7ae554..bc2d0ad73b8f7 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -373,10 +373,10 @@ private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, st internal sealed class FailingTextLoader : TextLoader { - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) { - Assert.True(false, $"Content of document {documentId} should never be loaded"); - throw ExceptionUtilities.Unreachable(); + Assert.True(false, $"Content of document should never be loaded"); + throw ExceptionUtilities.Unreachable; } } @@ -497,28 +497,28 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new FileTextLoader(sourceFileA.Path, encodingA), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA), filePath: sourceFileA.Path)); var documentIdB = DocumentId.CreateNewId(projectP.Id, debugName: "B"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdB, name: "B", - loader: new FileTextLoader(sourceFileB.Path, encodingB), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), filePath: sourceFileB.Path)); var documentIdC = DocumentId.CreateNewId(projectP.Id, debugName: "C"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdC, name: "C", - loader: new FileTextLoader(sourceFileC.Path, encodingC), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC), filePath: sourceFileC.Path)); var documentIdE = DocumentId.CreateNewId(projectP.Id, debugName: "E"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdE, name: "E", - loader: new FileTextLoader(sourceFileE.Path, encodingE), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE), filePath: sourceFileE.Path)); // check that are testing documents whose hash algorithm does not match the PDB (but the hash itself does): @@ -558,7 +558,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume // change content of B on disk again: sourceFileB.WriteAllText(sourceB3, encodingB); - solution = solution.WithDocumentTextLoader(documentIdB, new FileTextLoader(sourceFileB.Path, encodingB), PreservationMode.PreserveValue); + solution = solution.WithDocumentTextLoader(documentIdB, new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), PreservationMode.PreserveValue); EnterBreakState(debuggingSession); @@ -4399,7 +4399,7 @@ public async Task MultiSession() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new FileTextLoader(sourceFileA.Path, Encoding.UTF8), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var tasks = Enumerable.Range(0, 10).Select(async i => @@ -4485,7 +4485,7 @@ public async Task WatchHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new FileTextLoader(sourceFileA.Path, Encoding.UTF8), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var hotReload = new WatchHotReloadService(workspace.Services, ImmutableArray.Create("Baseline", "AddDefinitionToExistingType", "NewTypeDefinition")); @@ -4552,7 +4552,7 @@ public async Task UnitTestingHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new FileTextLoader(sourceFileA.Path, Encoding.UTF8), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var hotReload = new UnitTestingHotReloadService(workspace.Services); diff --git a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs index 152ecfa34bf32..6d8daec08ab71 100644 --- a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Implementation.Workspaces; using Microsoft.CodeAnalysis.Host; @@ -119,7 +120,7 @@ public async Task TestCreateFromTemporaryStorageWithEncoding() private static void TestCreateTextInferredEncoding(ITextFactoryService textFactoryService, byte[] bytes, Encoding? defaultEncoding, Encoding expectedEncoding) { using var stream = new MemoryStream(bytes); - var text = textFactoryService.CreateText(stream, defaultEncoding); + var text = textFactoryService.CreateText(stream, defaultEncoding, CancellationToken.None); Assert.Equal(expectedEncoding, text.Encoding); } } diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index 5ac6703cd3872..af07e370bad4b 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -209,7 +209,7 @@ internal TestDocumentLoader(TestHostDocument hostDocument, string text) _text = text; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create(), _hostDocument.FilePath)); } diff --git a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs index 9a04b4b293bda..62dfa0e2d7122 100644 --- a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.DecompiledSource; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PdbSourceDocument; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -278,7 +279,7 @@ private bool RemoveDocumentFromWorkspace(Workspace workspace, MetadataAsSourceGe var documentId = _openedDocumentIds.GetValueOrDefault(fileInfo); Contract.ThrowIfNull(documentId); - workspace.OnDocumentClosed(documentId, new FileTextLoader(fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding)); + workspace.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding)); workspace.OnProjectRemoved(documentId.ProjectId); _openedDocumentIds = _openedDocumentIds.RemoveKey(fileInfo); diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index 5729d27c551f0..ebf796543ad33 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -82,7 +82,7 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work generatedDocumentId, Path.GetFileName(TemporaryFilePath), filePath: TemporaryFilePath, - loader: loadFileFromDisk ? new FileTextLoader(TemporaryFilePath, Encoding) : null); + loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding) : null); var projectInfo = ProjectInfo.Create( projectId, diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 68937f8dc1734..a605e00539b3c 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -341,7 +341,7 @@ public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) { if (_fileToDocumentInfoMap.TryGetValue(filePath, out var info)) { - workspace.OnDocumentClosed(info.DocumentId, new FileTextLoader(filePath, info.Encoding)); + workspace.OnDocumentClosed(info.DocumentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, filePath, info.Encoding)); return true; } diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 47007320e4319..81f806be96697 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -91,7 +91,7 @@ public void TryRemoveMiscellaneousDocument(Uri uri) } } - private class SourceTextLoader : TextLoader + private sealed class SourceTextLoader : TextLoader { private readonly SourceText _sourceText; private readonly string _fileUri; @@ -102,7 +102,7 @@ public SourceTextLoader(SourceText sourceText, string fileUri) _fileUri = fileUri; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_sourceText, VersionStamp.Create(), _fileUri)); } } diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 17451f67d4886..843415ecff98f 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -119,7 +119,7 @@ DocumentInfo CreateDocumentInfo(string unmappedPath) DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, - loader: new FileTextLoader(mappedPath, parsedCommandLine.Encoding)); + loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding)); } } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 66dd83152b58e..47a498f3e13d3 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -325,7 +326,8 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy var documentId = DocumentId.CreateNewId(projectToAddTo.Id); - var forkedSolution = projectToAddTo.Solution.AddDocument(DocumentInfo.Create(documentId, filePath, loader: new FileTextLoader(filePath, defaultEncoding: null), filePath: filePath)); + var fileLoader = new WorkspaceFileTextLoader(projectToAddTo.Solution.Services, filePath, defaultEncoding: null); + var forkedSolution = projectToAddTo.Solution.AddDocument(DocumentInfo.Create(documentId, filePath, loader: fileLoader, filePath: filePath)); var addedDocument = forkedSolution.GetRequiredDocument(documentId); var globalOptions = _componentModel.GetService(); diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index d9d7f207046f5..a3ddb4882dcca 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs @@ -45,17 +45,17 @@ public void CloseDocument(TextDocument document, SourceText text) } } - private class PreviewTextLoader : TextLoader + private sealed class PreviewTextLoader : TextLoader { private readonly SourceText _text; internal PreviewTextLoader(SourceText documentText) => _text = documentText; - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) => TextAndVersion.Create(_text, VersionStamp.Create()); } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs index 1909d18b636a4..738ba82f0e16d 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -292,7 +292,8 @@ private ProjectInfo CreateProjectInfoForDocument(string filePath) var languageInformation = TryGetLanguageInformation(filePath); Contract.ThrowIfNull(languageInformation); - return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, new FileTextLoader(filePath, defaultEncoding: null), languageInformation, Services.SolutionServices, _metadataReferences); + var loader = new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null); + return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, loader, languageInformation, Services.SolutionServices, _metadataReferences); } private void DetachFromDocument(string moniker) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index b3c174ecbce2d..bc8f5db48e61a 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -92,7 +92,7 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta } var documentId = DocumentId.CreateNewId(_project.Id, fullPath); - var textLoader = new FileTextLoader(fullPath, defaultEncoding: null); + var textLoader = new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, fullPath, defaultEncoding: null); var documentInfo = DocumentInfo.Create( documentId, FileNameUtilities.GetFileName(fullPath), @@ -401,7 +401,7 @@ public async ValueTask ProcessRegularFileChangesAsync(ImmutableSegmentedList d.Id == documentId)) { - documentsToChange.Add((documentId, new FileTextLoader(filePath, defaultEncoding: null))); + documentsToChange.Add((documentId, new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, filePath, defaultEncoding: null))); } } } @@ -612,7 +612,7 @@ public SourceTextLoader(SourceTextContainer textContainer, string? filePath) _filePath = filePath; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_textContainer.CurrentText, VersionStamp.Create(), _filePath)); } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs index 9b28ed151b3e5..5e5098f1e4fd6 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.ComponentModelHost; @@ -317,18 +318,20 @@ private void TryClosingDocumentsForMoniker(string moniker) { if (w.IsDocumentOpen(documentId) && !_workspace._documentsNotFromFiles.Contains(documentId)) { + var loader = new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null); + if (w.CurrentSolution.ContainsDocument(documentId)) { - w.OnDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); + w.OnDocumentClosed(documentId, loader); } else if (w.CurrentSolution.ContainsAdditionalDocument(documentId)) { - w.OnAdditionalDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); + w.OnAdditionalDocumentClosed(documentId, loader); } else { Debug.Assert(w.CurrentSolution.ContainsAnalyzerConfigDocument(documentId)); - w.OnAnalyzerConfigDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); + w.OnAnalyzerConfigDocumentClosed(documentId, loader); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs index 60b213a0c8066..1c97ff18ce64f 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs @@ -51,29 +51,5 @@ public async Task> GetRemoteProjectInfosAsync(C return projectInfos; } - - public static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files) - { - var projectId = ProjectId.CreateNewId(); - var docInfos = ImmutableArray.CreateBuilder(); - - foreach (var file in files) - { - var fileName = Path.GetFileNameWithoutExtension(file); - var docInfo = DocumentInfo.Create(DocumentId.CreateNewId(projectId), - fileName, - filePath: file, - loader: new FileTextLoaderNoException(file, null)); - docInfos.Add(docInfo); - } - - return ProjectInfo.Create( - projectId, - VersionStamp.Create(), - projectName, - projectName, - language, - documents: docInfos.ToImmutable()); - } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs index fd319980dbf00..9372dc858b60c 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs @@ -12,6 +12,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LiveShare.LanguageServices; @@ -75,14 +76,14 @@ public async Task> GetRemoteProjectInfosAsync(Cancel .Where(f => !_secondaryBufferFileExtensions.Any(ext => f.LocalPath.EndsWith(ext))) .Select(f => lspClient.ProtocolConverter.FromProtocolUriAsync(f, false, cancellationToken)); var files = await Task.WhenAll(filesTasks).ConfigureAwait(false); - var projectInfo = CreateProjectInfo(project.Name, project.Language, files.Select(f => f.LocalPath).ToImmutableArray()); + var projectInfo = CreateProjectInfo(project.Name, project.Language, files.Select(f => f.LocalPath).ToImmutableArray(), _remoteLanguageServiceWorkspace.Services.SolutionServices); projectInfos.Add(projectInfo); } return projectInfos.ToImmutableArray(); } - private static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files) + private static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files, SolutionServices services) { var projectId = ProjectId.CreateNewId(); var docInfos = ImmutableArray.CreateBuilder(); @@ -93,7 +94,7 @@ private static ProjectInfo CreateProjectInfo(string projectName, string language var docInfo = DocumentInfo.Create(DocumentId.CreateNewId(projectId), fileName, filePath: file, - loader: new FileTextLoaderNoException(file, null)); + loader: new WorkspaceFileTextLoaderNoException(services, file, defaultEncoding: null)); docInfos.Add(docInfo); } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/FileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs similarity index 71% rename from src/VisualStudio/LiveShare/Impl/Client/Projects/FileTextLoaderNoException.cs rename to src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs index 5b6a7551133aa..984b7d28617a5 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/FileTextLoaderNoException.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects @@ -17,20 +18,21 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects /// This is a FileTextLoader which no-ops if the file is not available on disk. This is the common case for /// Cascade and throwing exceptions slows down GetText operations significantly enough to have visible UX impact. /// - internal class FileTextLoaderNoException : FileTextLoader + internal sealed class WorkspaceFileTextLoaderNoException : WorkspaceFileTextLoader { - public FileTextLoaderNoException(string path, Encoding defaultEncoding) : base(path, defaultEncoding) + public WorkspaceFileTextLoaderNoException(SolutionServices services, string path, Encoding defaultEncoding) + : base(services, path, defaultEncoding) { } - public override Task LoadTextAndVersionAsync(CodeAnalysis.Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) { if (!File.Exists(Path)) { return Task.FromResult(TextAndVersion.Create(SourceText.From(""), VersionStamp.Create())); } - return base.LoadTextAndVersionAsync(workspace, documentId, cancellationToken); + return base.LoadTextAndVersionAsync(cancellationToken); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index 64c4184ab37b7..b2284268131dc 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Options; @@ -335,10 +336,12 @@ private Document AddDocumentToProject(string filePath, string language, string p project = CurrentSolution.GetRequiredProject(projectInfo.Id); } - var docInfo = DocumentInfo.Create(DocumentId.CreateNewId(project.Id), - name: Path.GetFileName(filePath), - loader: new FileTextLoader(filePath, null), - filePath: filePath); + var docInfo = DocumentInfo.Create( + DocumentId.CreateNewId(project.Id), + name: Path.GetFileName(filePath), + loader: new WorkspaceFileTextLoader(project.Solution.Services, filePath, defaultEncoding: null), + filePath: filePath); + OnDocumentAdded(docInfo); return CurrentSolution.GetDocument(docInfo.Id)!; } @@ -371,7 +374,7 @@ public void NotifyOnDocumentClosing(string moniker) // check if the doc is part of the current Roslyn workspace before notifying Roslyn. if (CurrentSolution.ContainsProject(id.ProjectId)) { - OnDocumentClosed(id, new FileTextLoaderNoException(moniker, null)); + OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(CurrentSolution.Services, moniker, defaultEncoding: null)); _openedDocs = _openedDocs.Remove(moniker); } } diff --git a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt index 8b137891791fe..791ccc8106fdb 100644 --- a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt @@ -1 +1,4 @@ - +virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) +override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) +*REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) +*REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index e61eec56067ab..5a47611921f87 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -441,7 +441,7 @@ private IEnumerable ResolveAnalyzerReferences(CommandLineArgu return commandLineArgs.ResolveAnalyzerReferences(analyzerLoader).Distinct(AnalyzerReferencePathComparer.Instance); } - private static ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) + private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) { var results = ImmutableArray.CreateBuilder(); @@ -454,7 +454,7 @@ private static ImmutableArray CreateDocumentInfos(IReadOnlyList ResolveReferencesAsync(ProjectId id, Proj // First, gather all of the metadata references from the command-line arguments. var resolvedMetadataReferences = commandLineArgs.ResolveMetadataReferences( new WorkspaceMetadataFileReferenceResolver( - metadataService: GetWorkspaceService(), + metadataService: _workspaceServices.GetRequiredService(), pathResolver: new RelativePathResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory))); var builder = new ResolvedReferencesBuilder(resolvedMetadataReferences); diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs index dba94bbc991dd..c480a89edb51d 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs @@ -452,7 +452,7 @@ protected override void ApplyDocumentAdded(DocumentInfo info, SourceText text) var newDocumentInfo = info.WithName(fileName) .WithFilePath(fullPath) - .WithTextLoader(new FileTextLoader(fullPath, text.Encoding)); + .WithTextLoader(new WorkspaceFileTextLoader(Services.SolutionServices, fullPath, text.Encoding)); // add document to project file _applyChangesProjectFile.AddDocument(relativePath); diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 9d91665102568..5538f16a6c4be 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -29,3 +29,9 @@ Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentOpenedEventAsync(Microsoft.Cod Microsoft.CodeAnalysis.Workspace.TextDocumentClosed -> System.EventHandler Microsoft.CodeAnalysis.Workspace.TextDocumentOpened -> System.EventHandler static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers +virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText +override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +*REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +*REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs index 97d533a846920..96b391eaf3b48 100644 --- a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs @@ -127,7 +127,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, name: name, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, - loader: new FileTextLoader(absolutePath, commandLineArguments.Encoding), + loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), filePath: absolutePath); docs.Add(doc); @@ -154,7 +154,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, name: name, folders: folders, sourceCodeKind: SourceCodeKind.Regular, - loader: new FileTextLoader(absolutePath, commandLineArguments.Encoding), + loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), filePath: absolutePath); additionalDocs.Add(doc); diff --git a/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs index 40bd368fd8c6f..987c9f3950b39 100644 --- a/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs @@ -58,18 +58,29 @@ public FileTextLoader(string path, Encoding? defaultEncoding) internal sealed override string FilePath => Path; - protected virtual SourceText CreateText(Stream stream, Workspace workspace) - { - var factory = workspace.Services.GetRequiredService(); - return factory.CreateText(stream, DefaultEncoding); - } + /// + /// Creates from . + /// + /// Stream. + /// Obsolete. Null. + [Obsolete("Use CreateText(Stream, CancellationToken)")] + protected virtual SourceText CreateText(Stream stream, Workspace? workspace) + => EncodedStringText.Create(stream, DefaultEncoding); + + /// + /// Creates from . + /// + protected virtual SourceText CreateText(Stream stream, CancellationToken cancellationToken) +#pragma warning disable CS0618 // Type or member is obsolete + => CreateText(stream, workspace: null); +#pragma warning restore /// /// Load a text and a version of the document in the workspace. /// /// /// - public override async Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override async Task LoadTextAndVersionAsync(CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -150,7 +161,7 @@ public override async Task LoadTextAndVersionAsync(Workspace wor // we do this so that we asynchronously read from file. and this should allocate less for IDE case. // but probably not for command line case where it doesn't use more sophisticated services. using var readStream = await SerializableBytes.CreateReadableStreamAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); - var text = CreateText(readStream, workspace); + var text = CreateText(readStream, cancellationToken); textAndVersion = TextAndVersion.Create(text, version, Path); } @@ -169,11 +180,11 @@ public override async Task LoadTextAndVersionAsync(Workspace wor } /// - /// Load a text and a version of the document in the workspace. + /// Load a text and a version of the document. /// /// /// - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -185,7 +196,7 @@ internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace works using (var stream = FileUtilities.RethrowExceptionsAsIOException(() => new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, bufferSize: 4096, useAsync: false))) { var version = VersionStamp.Create(prevLastWriteTime); - var text = CreateText(stream, workspace); + var text = CreateText(stream, cancellationToken); textAndVersion = TextAndVersion.Create(text, version, Path); } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs index 6f555c5abd674..16b6f81221427 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs @@ -29,7 +29,7 @@ internal interface ITextFactoryService : IWorkspaceService /// is null and the stream appears to be a binary file. /// /// An IO error occurred while reading from the stream. - SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken = default); + SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken); /// /// Creates from a reader with given . @@ -38,7 +38,7 @@ internal interface ITextFactoryService : IWorkspaceService /// Specifies an encoding for the SourceText. /// it could be null. but if null is given, it won't be able to calculate checksum /// Cancellation token. - SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken = default); + SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs index ae3a638b03f00..9643ae18bda1c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Host { [ExportWorkspaceService(typeof(ITextFactoryService), ServiceLayer.Default), Shared] - internal class TextFactoryService : ITextFactoryService + internal sealed class TextFactoryService : ITextFactoryService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -22,13 +22,13 @@ public TextFactoryService() { } - public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken = default) + public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return EncodedStringText.Create(stream, defaultEncoding); } - public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken = default) + public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs index 6e2e2778682de..89a34ac2e36f6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis @@ -60,8 +59,7 @@ public IEnumerable GetRemovedProjects() public IEnumerable GetAddedAnalyzerReferences() { - using var _ = PooledHashSet.GetInstance(out var oldAnalyzerReferences); - oldAnalyzerReferences.UnionWith(_oldSolution.AnalyzerReferences); + var oldAnalyzerReferences = new HashSet(_oldSolution.AnalyzerReferences); foreach (var analyzerReference in _newSolution.AnalyzerReferences) { if (!oldAnalyzerReferences.Contains(analyzerReference)) @@ -73,8 +71,7 @@ public IEnumerable GetAddedAnalyzerReferences() public IEnumerable GetRemovedAnalyzerReferences() { - using var _ = PooledHashSet.GetInstance(out var newAnalyzerReferences); - newAnalyzerReferences.UnionWith(_newSolution.AnalyzerReferences); + var newAnalyzerReferences = new HashSet(_newSolution.AnalyzerReferences); foreach (var analyzerReference in _oldSolution.AnalyzerReferences) { if (!newAnalyzerReferences.Contains(analyzerReference)) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index ae98692975601..3c1a4836d573e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -74,7 +74,7 @@ public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) info.Attributes, sourceText: null, textAndVersionSource: info.TextLoader != null - ? CreateRecoverableText(info.TextLoader, info.Id, services) + ? CreateRecoverableText(info.TextLoader, services.SolutionServices) : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, Encoding.UTF8), VersionStamp.Default, info.FilePath))) { } @@ -87,12 +87,9 @@ public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) protected static ValueSource CreateStrongText(TextAndVersion text) => new ConstantValueSource(text); - protected static ValueSource CreateStrongText(TextLoader loader, DocumentId documentId, HostWorkspaceServices services) + protected static ValueSource CreateStrongText(TextLoader loader) { - return new AsyncLazy( - asynchronousComputeFunction: cancellationToken => loader.LoadTextAsync(services.Workspace, documentId, cancellationToken), - synchronousComputeFunction: cancellationToken => loader.LoadTextSynchronously(services.Workspace, documentId, cancellationToken), - cacheResult: true); + return new AsyncLazy(loader.LoadTextAsync, loader.LoadTextSynchronously, cacheResult: true); } protected static ValueSource CreateRecoverableText(TextAndVersion text, SolutionServices services) @@ -110,15 +107,8 @@ protected static ValueSource CreateRecoverableText(TextAndVersio return result; } - protected static ValueSource CreateRecoverableText(TextLoader loader, DocumentId documentId, HostWorkspaceServices services) - { - return new RecoverableTextAndVersion( - new AsyncLazy( - asynchronousComputeFunction: cancellationToken => loader.LoadTextAsync(services.Workspace, documentId, cancellationToken), - synchronousComputeFunction: cancellationToken => loader.LoadTextSynchronously(services.Workspace, documentId, cancellationToken), - cacheResult: false), - services.SolutionServices); - } + protected static ValueSource CreateRecoverableText(TextLoader loader, SolutionServices services) + => new RecoverableTextAndVersion(new AsyncLazy(loader.LoadTextAsync, loader.LoadTextSynchronously, cacheResult: false), services); public ITemporaryTextStorageInternal? Storage => (TextAndVersionSource as RecoverableTextAndVersion)?.Storage; @@ -230,8 +220,8 @@ public TextDocumentState UpdateText(TextLoader loader, PreservationMode mode) { // don't blow up on non-text documents. var newTextSource = mode == PreservationMode.PreserveIdentity - ? CreateStrongText(loader, Id, solutionServices) - : CreateRecoverableText(loader, Id, solutionServices); + ? CreateStrongText(loader) + : CreateRecoverableText(loader, solutionServices.SolutionServices); return UpdateText(newTextSource, mode, incremental: false); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index b1895bb604f10..d76523abbbbac 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -29,7 +30,22 @@ public abstract class TextLoader /// /// /// - public abstract Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken); + public virtual Task LoadTextAndVersionAsync(CancellationToken cancellationToken) +#pragma warning disable CS0618 // Type or member is obsolete + => LoadTextAndVersionAsync(workspace: null, documentId: null, cancellationToken); +#pragma warning restore CS0618 + + /// + /// Load a text and a version of the document. + /// + /// Obsolete. Null. + /// Obsolete. Null. + /// + /// + /// + [Obsolete("Use LoadTextAndVersionAsync(CancellationToken) instead")] + public virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => throw new NotImplementedException("The API is obsolete, call LoadTextAndVersionAsync(CancellationToken) instead"); /// /// Load a text and a version of the document in the workspace. @@ -37,13 +53,13 @@ public abstract class TextLoader /// /// /// - internal virtual TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal virtual TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) { // this implementation exists in case a custom derived type does not have access to internals - return LoadTextAndVersionAsync(workspace, documentId, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); + return LoadTextAndVersionAsync(cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); } - internal async Task LoadTextAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal async Task LoadTextAsync(CancellationToken cancellationToken) { var retries = 0; @@ -51,20 +67,20 @@ internal async Task LoadTextAsync(Workspace workspace, DocumentI { try { - return await LoadTextAndVersionAsync(workspace, documentId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); + return await LoadTextAndVersionAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } catch (IOException e) { if (++retries > MaxRetries) { - return CreateFailedText(workspace, documentId, e.Message); + return CreateFailedText(e.Message); } // fall out to try again } catch (InvalidDataException e) { - return CreateFailedText(workspace, documentId, e.Message); + return CreateFailedText(e.Message); } // try again after a delay @@ -72,7 +88,7 @@ internal async Task LoadTextAsync(Workspace workspace, DocumentI } } - internal TextAndVersion LoadTextSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal TextAndVersion LoadTextSynchronously(CancellationToken cancellationToken) { var retries = 0; @@ -80,32 +96,31 @@ internal TextAndVersion LoadTextSynchronously(Workspace workspace, DocumentId do { try { - return LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken); + return LoadTextAndVersionSynchronously(cancellationToken); } catch (IOException e) { if (++retries > MaxRetries) { - return CreateFailedText(workspace, documentId, e.Message); + return CreateFailedText(e.Message); } // fall out to try again } catch (InvalidDataException e) { - return CreateFailedText(workspace, documentId, e.Message); + return CreateFailedText(e.Message); } + cancellationToken.ThrowIfCancellationRequested(); + // try again after a delay Thread.Sleep(RetryDelay); } } - private TextAndVersion CreateFailedText(Workspace workspace, DocumentId documentId, string message) + private TextAndVersion CreateFailedText(string message) { - // Notify workspace for backwards compatibility. - workspace.OnWorkspaceFailed(new DocumentDiagnostic(WorkspaceDiagnosticKind.Failure, message, documentId)); - Location location; string display; @@ -114,7 +129,7 @@ private TextAndVersion CreateFailedText(Workspace workspace, DocumentId document if (filePath == null) { location = Location.None; - display = documentId.ToString(); + display = ""; } else { @@ -164,10 +179,10 @@ private sealed class TextDocumentLoader : TextLoader internal TextDocumentLoader(TextAndVersion textAndVersion) => _textAndVersion = textAndVersion; - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => Task.FromResult(_textAndVersion); - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) => _textAndVersion; } @@ -184,10 +199,10 @@ internal TextContainerLoader(SourceTextContainer container, VersionStamp version _filePath = filePath; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) => TextAndVersion.Create(_container.CurrentText, _version, _filePath); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 84d1aadaea591..2548c9cbe32b7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -372,6 +372,7 @@ protected virtual void Dispose(bool finalize) } #region Host API + /// /// Call this method to respond to a solution being opened in the host environment. /// diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs new file mode 100644 index 0000000000000..ca312116d805b --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis +{ + /// + /// that uses workspace services (i.e. ) to load file content. + /// + [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] + internal class WorkspaceFileTextLoader : FileTextLoader + { + private readonly ITextFactoryService _textFactory; + + internal WorkspaceFileTextLoader(SolutionServices services, string path, Encoding? defaultEncoding) +#pragma warning disable RS0030 // Do not used banned APIs + : base(path, defaultEncoding) +#pragma warning restore + { + _textFactory = services.GetRequiredService(); + } + + protected override SourceText CreateText(Stream stream, CancellationToken cancellationToken) + => _textFactory.CreateText(stream, DefaultEncoding, cancellationToken); + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 8cc872de34689..08c8388203e15 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -462,7 +462,7 @@ internal void OnSourceGeneratedDocumentClosed(SourceGeneratedDocument document) } } - private class ReuseVersionLoader : TextLoader + private sealed class ReuseVersionLoader : TextLoader { // Capture DocumentState instead of Document so that we don't hold onto the old solution. private readonly DocumentState _oldDocumentState; @@ -474,8 +474,7 @@ public ReuseVersionLoader(DocumentState oldDocumentState, SourceText newText) _newText = newText; } - public override async Task LoadTextAndVersionAsync( - Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override async Task LoadTextAndVersionAsync(CancellationToken cancellationToken) { var oldText = await _oldDocumentState.GetTextAsync(cancellationToken).ConfigureAwait(false); var version = await _oldDocumentState.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); @@ -483,7 +482,7 @@ public override async Task LoadTextAndVersionAsync( return GetProperTextAndVersion(oldText, _newText, version, _oldDocumentState.FilePath); } - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) { var oldText = _oldDocumentState.GetTextSynchronously(cancellationToken); var version = _oldDocumentState.GetTextVersionSynchronously(cancellationToken); diff --git a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs index 572bb50a9abf4..f470c4cc54d52 100644 --- a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.IO; using System.Linq; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests @@ -28,7 +29,7 @@ public void Create_Errors() [Fact] public void Create() { - var loader = new FileTextLoader(Path.GetTempPath(), defaultEncoding: null); + var loader = new TestTextLoader("text"); var id = DocumentId.CreateNewId(ProjectId.CreateNewId()); var info = DocumentInfo.Create( @@ -83,7 +84,7 @@ public void TestProperties() SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithId(value), opt => opt.Id, documentId, defaultThrows: true); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithName(value), opt => opt.Name, "New", defaultThrows: true); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithSourceCodeKind(value), opt => opt.SourceCodeKind, SourceCodeKind.Script); - SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithTextLoader(value), opt => opt.TextLoader, (TextLoader)new FileTextLoader(Path.GetTempPath(), defaultEncoding: null)); + SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithTextLoader(value), opt => opt.TextLoader, (TextLoader)new TestTextLoader("text")); SolutionTestHelpers.TestListProperty(instance, (old, value) => old.WithFolders(value), opt => opt.Folders, "folder", allowDuplicates: true); } diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 846b69a3b252e..131038d8a0c9b 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -1926,7 +1926,7 @@ public void TestDocumentChangedOnDiskIsNotObserved() var did = DocumentId.CreateNewId(pid); sol = sol.AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new FileTextLoader(file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); var observedText = GetObservedText(sol, did, text1); @@ -1985,7 +1985,7 @@ public void TestGetLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new FileTextLoader(file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); var doc = sol.GetDocument(did); @@ -2052,7 +2052,7 @@ public void TestGetSyntaxTreeFromLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new FileTextLoader(file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); var doc = sol.GetDocument(did); var docTree = doc.GetSyntaxTreeAsync().Result; @@ -2567,17 +2567,11 @@ public async Task TestDocumentFileAccessFailureMissingFile() var workspace = new AdhocWorkspace(); var solution = workspace.CurrentSolution; - WorkspaceDiagnostic diagnosticFromEvent = null; - solution.Workspace.WorkspaceFailed += (sender, args) => - { - diagnosticFromEvent = args.Diagnostic; - }; - var pid = ProjectId.CreateNewId(); var did = DocumentId.CreateNewId(pid); solution = solution.AddProject(pid, "goo", "goo", LanguageNames.CSharp) - .AddDocument(did, "x", new FileTextLoader(@"C:\doesnotexist.cs", Encoding.UTF8)) + .AddDocument(did, "x", new WorkspaceFileTextLoader(solution.Services, @"C:\doesnotexist.cs", Encoding.UTF8)) .WithDocumentFilePath(did, "document path"); var doc = solution.GetDocument(did); @@ -2586,7 +2580,6 @@ public async Task TestDocumentFileAccessFailureMissingFile() var diagnostic = await doc.State.GetLoadDiagnosticAsync(CancellationToken.None).ConfigureAwait(false); Assert.Equal(@"C:\doesnotexist.cs: (0,0)-(0,0)", diagnostic.Location.GetLineSpan().ToString()); - Assert.Equal(WorkspaceDiagnosticKind.Failure, diagnosticFromEvent.Kind); Assert.Equal("", text.ToString()); // Verify invariant: The compilation is guaranteed to have a syntax tree for each document of the project (even if the contnet fails to load). @@ -2843,7 +2836,9 @@ public void TestProjectCompletenessWithMultipleProjects() private class TestSmallFileTextLoader : FileTextLoader { public TestSmallFileTextLoader(string path, Encoding encoding) +#pragma warning disable RS0030 // Do not used banned APIs : base(path, encoding) +#pragma warning restore { } @@ -2854,8 +2849,6 @@ public TestSmallFileTextLoader(string path, Encoding encoding) [Fact] public async Task TestMassiveFileSize() { - var workspace = new AdhocWorkspace(); - using var root = new TempRoot(); var file = root.CreateFile(prefix: "massiveFile", extension: ".cs").WriteAllText("hello"); @@ -2869,7 +2862,7 @@ public async Task TestMassiveFileSize() try { // test async one - var unused = await loader.LoadTextAndVersionAsync(workspace, DocumentId.CreateNewId(ProjectId.CreateNewId()), CancellationToken.None); + var unused = await loader.LoadTextAndVersionAsync(CancellationToken.None); } catch (InvalidDataException ex) { @@ -2883,7 +2876,7 @@ public async Task TestMassiveFileSize() try { // test sync one - var unused = loader.LoadTextAndVersionSynchronously(workspace, DocumentId.CreateNewId(ProjectId.CreateNewId()), CancellationToken.None); + var unused = loader.LoadTextAndVersionSynchronously(CancellationToken.None); } catch (InvalidDataException ex) { diff --git a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs index 3b74790d6145a..9845f3a4f75c3 100644 --- a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs +++ b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs @@ -18,7 +18,7 @@ internal class TestTextLoader : TextLoader public TestTextLoader(string text = "test") => _text = text; - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create())); } } diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 20de2f3eca292..610f6b4b3d879 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -2326,10 +2326,10 @@ public async Task TestLoadTextSync() var infos = await loader.LoadProjectInfoAsync(projectFullPath); var doc = infos[0].Documents[0]; - var tav = doc.TextLoader.LoadTextAndVersionSynchronously(workspace, doc.Id, CancellationToken.None); + var tav = doc.TextLoader.LoadTextAndVersionSynchronously(CancellationToken.None); var adoc = infos[0].AdditionalDocuments.First(a => a.Name == "XamlFile.xaml"); - var atav = adoc.TextLoader.LoadTextAndVersionSynchronously(workspace, adoc.Id, CancellationToken.None); + var atav = adoc.TextLoader.LoadTextAndVersionSynchronously(CancellationToken.None); Assert.Contains("Window", atav.Text.ToString(), StringComparison.Ordinal); } From 467567ff5d40c415399e32860c75a871a6ba3384 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 21 Sep 2022 11:55:34 -0700 Subject: [PATCH 02/32] Implement override detection --- .../Portable/Workspace/Solution/TextLoader.cs | 18 ++- .../CoreTest/SolutionTests/TextLoaderTests.cs | 137 ++++++++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index d76523abbbbac..a4fb2434a711e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Reflection; using System.Text; @@ -18,6 +20,8 @@ namespace Microsoft.CodeAnalysis /// public abstract class TextLoader { + private static ImmutableDictionary s_isObsoleteLoadTextAndVersionAsyncOverriden = ImmutableDictionary.Empty; + private const double MaxDelaySecs = 1.0; private const int MaxRetries = 5; internal static readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(MaxDelaySecs / MaxRetries); @@ -31,9 +35,19 @@ public abstract class TextLoader /// /// public virtual Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + { #pragma warning disable CS0618 // Type or member is obsolete - => LoadTextAndVersionAsync(workspace: null, documentId: null, cancellationToken); -#pragma warning restore CS0618 + if (ImmutableInterlocked.GetOrAdd( + ref s_isObsoleteLoadTextAndVersionAsyncOverriden, + GetType(), + new Func>(LoadTextAndVersionAsync).Method.DeclaringType != typeof(TextLoader))) + { + return LoadTextAndVersionAsync(workspace: null, documentId: null, cancellationToken); + } +#pragma warning restore + + throw new NotImplementedException($"{GetType()} must override LoadTextAndVersionAsync"); + } /// /// Load a text and a version of the document. diff --git a/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs new file mode 100644 index 0000000000000..117532c6db3e6 --- /dev/null +++ b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests; + +public class TextLoaderTests +{ + private class LoaderNoOverride1 : TextLoader + { + } + + private class LoaderNoOverride2 : TextLoader + { + public new virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult((TextAndVersion?)null!); + } + + private class LoaderNoOverrideBase : TextLoader + { + // newslot + public new virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult((TextAndVersion?)null!); + + // newslot + public virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId) + => Task.FromResult((TextAndVersion?)null!); + + // newslot + public virtual Task LoadTextAndVersionAsync(Workspace? workspace, ref DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult((TextAndVersion?)null!); + + // newslot + public virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult((TextAndVersion?)null!); + } + + private class LoaderNoOverride3 : LoaderNoOverrideBase + { + public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult((TextAndVersion?)null!); + } + + private class LoaderNoOverride4 : LoaderNoOverrideBase + { + public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId) + => base.LoadTextAndVersionAsync(workspace, documentId); + } + + private class LoaderNoOverride5 : LoaderNoOverrideBase + { + public override Task LoadTextAndVersionAsync(Workspace? workspace, ref DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult((TextAndVersion?)null!); + } + + private class LoaderNoOverride6 : LoaderNoOverrideBase + { + public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult((TextAndVersion?)null!); + } + + public static IEnumerable GetNoOverideLoaders() + { + yield return new[] { new LoaderNoOverride1() }; + yield return new[] { new LoaderNoOverride2() }; + yield return new[] { new LoaderNoOverrideBase() }; + yield return new[] { new LoaderNoOverride3() }; + yield return new[] { new LoaderNoOverride4() }; + yield return new[] { new LoaderNoOverride5() }; + yield return new[] { new LoaderNoOverride6() }; + } + + private class LoaderOverridesObsolete : TextLoader + { + public static readonly TextAndVersion Value = TextAndVersion.Create(SourceText.From(""), VersionStamp.Default); + + [Obsolete] + public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult(Value); + } + + private class LoaderOverridesObsolete2 : LoaderOverridesObsolete + { + public static new readonly TextAndVersion Value = TextAndVersion.Create(SourceText.From(""), VersionStamp.Default); + + [Obsolete] + public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => Task.FromResult(Value); + } + + private class LoaderOverridesNew : TextLoader + { + public static readonly TextAndVersion Value = TextAndVersion.Create(SourceText.From(""), VersionStamp.Default); + + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + => Task.FromResult(Value); + } + + [Theory, Obsolete] + [MemberData(nameof(GetNoOverideLoaders))] + public async Task NoOverride(TextLoader loader) + { + await Assert.ThrowsAsync(() => loader.LoadTextAndVersionAsync(CancellationToken.None)); + await Assert.ThrowsAsync(() => loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); + } + + [Fact, Obsolete] + public async Task OverridesObsolete() + { + var loader = new LoaderOverridesObsolete(); + Assert.Same(LoaderOverridesObsolete.Value, await loader.LoadTextAndVersionAsync(CancellationToken.None)); + Assert.Same(LoaderOverridesObsolete.Value, await loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); + } + + [Fact, Obsolete] + public async Task OverridesObsolete2() + { + var loader = new LoaderOverridesObsolete2(); + Assert.Same(LoaderOverridesObsolete2.Value, await loader.LoadTextAndVersionAsync(CancellationToken.None)); + Assert.Same(LoaderOverridesObsolete2.Value, await loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); + } + + [Fact, Obsolete] + public async Task OverridesNew() + { + var loader = new LoaderOverridesNew(); + Assert.Same(LoaderOverridesNew.Value, await loader.LoadTextAndVersionAsync(CancellationToken.None)); + Assert.Same(LoaderOverridesNew.Value, await loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); + } +} From 8e2683af2b885a54a613fab91620bcf6e8ad33f5 Mon Sep 17 00:00:00 2001 From: tmat Date: Tue, 26 Jul 2022 19:04:47 -0700 Subject: [PATCH 03/32] Add ChecksumAlgorithm to DocumentAttributes and ProjectAttributes --- eng/config/BannedSymbols.txt | 13 +- eng/targets/Imports.targets | 4 +- .../CommandLine/CSharpCommandLineParser.cs | 2 +- .../CSharp/Portable/PublicAPI.Unshipped.txt | 6 + .../Portable/Syntax/CSharpSyntaxNode.cs | 2 + .../Portable/Syntax/CSharpSyntaxTree.Dummy.cs | 14 +- .../Portable/Syntax/CSharpSyntaxTree.cs | 4 +- .../CSharp/Portable/Syntax/SyntaxFactory.cs | 3 +- .../CSharp/Test/Emit/PDB/PDBTests.cs | 26 ++ .../Test/Syntax/Syntax/SyntaxTreeTests.cs | 48 ++- .../Analyzers/AnalyzerFileReferenceTests.cs | 3 +- .../Diagnostics/OperationTests.cs | 3 +- .../SuppressMessageAttributeCompilerTests.cs | 8 +- ...uppressMessageTargetSymbolResolverTests.cs | 6 +- .../CodeAnalysisTest/EmbeddedTextTests.cs | 16 +- .../Core/CodeAnalysisTest/GivesAccessTo.cs | 10 +- .../IsSymbolAccessibleWithinTests.cs | 6 +- .../MetadataReferenceTests.cs | 28 +- .../Microsoft.CodeAnalysis.UnitTests.csproj | 2 + .../CodeAnalysisTest/Text/SourceTextTests.cs | 22 +- .../CodeAnalysisTest/Text/TextChangeTests.cs | 10 +- .../MSBuildTask/Microsoft.CSharp.Core.targets | 2 +- .../Microsoft.VisualBasic.Core.targets | 2 +- .../Portable/CryptographicHashProvider.cs | 4 +- src/Compilers/Core/Portable/EmbeddedText.cs | 1 - .../Portable/Microsoft.CodeAnalysis.csproj | 1 + .../Core/Portable/PEWriter/DebugSourceInfo.cs | 1 - .../Core/Portable/Syntax/SyntaxNode.cs | 3 +- .../Core/Portable/Text/SourceHashAlgorithm.cs | 5 - .../SourceHashAlgorithms.cs | 7 +- .../Core/Portable/Text/SourceText.cs | 1 - .../CSharpDeterministicKeyBuilderTests.cs | 6 +- .../AnalyzerConsistencyCheckerTests.cs | 4 +- .../VBCSCompiler.UnitTests.csproj | 1 + .../Test/Core/AssemblyLoadTestFixture.cs | 3 +- src/Compilers/Test/Core/CommonTestBase.cs | 5 +- .../Core/Compilation/CompilationExtensions.cs | 3 +- .../Test/Core/Platform/Desktop/TestHelpers.cs | 3 +- src/Compilers/Test/Core/TestBase.cs | 2 +- .../Utilities/CSharp/BasicCompilationUtils.cs | 3 +- .../Test/Utilities/CSharp/CSharpTestBase.cs | 20 +- .../Test/Utilities/CSharp/CSharpTestSource.cs | 29 +- .../CSharp/DiagnosticTestUtilities.cs | 3 +- .../Utilities/VisualBasic/BasicTestSource.vb | 27 +- .../VisualBasic/CompilationTestUtils.vb | 4 +- .../Test/Utilities/VisualBasic/VBParser.vb | 3 +- .../Portable/Binding/Binder_Expressions.vb | 3 +- .../VisualBasicCommandLineParser.vb | 2 +- .../Compilation/VisualBasicCompilation.vb | 6 +- .../EmbeddedSymbols/EmbeddedSymbolManager.vb | 17 +- ...MyGroupCollectionPropertyAccessorSymbol.vb | 3 +- .../Portable/Syntax/VisualBasicSyntaxNode.vb | 2 + ...isualBasicSyntaxTree.DebuggerSyntaxTree.vb | 32 ++ .../VisualBasicSyntaxTree.DummySyntaxTree.vb | 10 +- .../VisualBasicSyntaxTree.ParsedSyntaxTree.vb | 2 +- .../Portable/Syntax/VisualBasicSyntaxTree.vb | 14 +- .../VisualBasic/Test/Emit/PDB/PDBTests.vb | 32 ++ .../Semantic/Compilation/MyTemplateTests.vb | 7 +- .../SymbolsTests/Source/GroupClassTests.vb | 42 +- .../Test/Syntax/Syntax/SyntaxTreeTests.vb | 39 +- .../CSharpDecompiledSourceService.cs | 2 +- ...nteractiveEvaluatorLanguageInfoProvider.cs | 3 +- ...harpSendToInteractiveSubmissionProvider.cs | 2 +- .../RazorLineFormattingOptionsTests.cs | 10 +- .../PdbSourceDocumentLoaderServiceTests.cs | 2 +- .../Core/Interactive/InteractiveSession.cs | 20 +- .../Workspaces/EditorTextFactoryService.cs | 14 +- .../CompileTimeSolutionProviderTests.cs | 6 +- ...itAndContinueMethodDebugInfoReaderTests.cs | 1 + .../EditAndContinueWorkspaceServiceTests.cs | 70 ++-- .../Test/Workspaces/TextFactoryTests.cs | 2 +- .../ActiveStatementTestHelpers.cs | 2 +- .../EditAndContinueTestHelpers.cs | 11 +- .../AbstractLanguageServerProtocolTests.cs | 12 +- .../TestUtilities/Rename/RenamerTests.cs | 14 +- .../Workspaces/TestHostDocument.cs | 21 +- .../Workspaces/TestHostProject.cs | 29 +- .../TestWorkspace_XmlConsumption.cs | 6 +- .../Text/Extensions.SnapshotSourceText.cs | 9 +- src/EditorFeatures/Text/Extensions.cs | 4 +- .../ExpressionCompiler/SyntaxHelpers.cs | 10 +- .../ExpressionCompiler/SyntaxHelpers.vb | 32 +- .../Configuration/ConfigurationUpdater.cs | 22 +- .../EditAndContinue/CommittedSolution.cs | 8 +- .../AbstractGenerateTypeService.Editor.cs | 17 +- ...compilationMetadataAsSourceFileProvider.cs | 2 +- .../MetadataAsSourceGeneratedFileInfo.cs | 25 +- .../Microsoft.CodeAnalysis.Features.csproj | 2 +- .../DocumentDebugInfoReader.cs | 1 + .../IPdbSourceDocumentLoaderService.cs | 4 +- .../PdbSourceDocumentLoaderService.cs | 4 +- ...rceDocumentMetadataAsSourceFileProvider.cs | 42 +- .../Workspace/MiscellaneousFileUtilities.cs | 31 +- .../Handler/DocumentChanges/DidOpenHandler.cs | 4 +- .../LspMiscellaneousFilesWorkspace.cs | 12 +- .../Workspaces/LspWorkspaceManagerTests.cs | 10 +- .../Lsif/Generator/CompilerInvocation.cs | 26 +- src/Scripting/CSharp/CSharpScript.cs | 2 +- .../TestCompilationFactory.cs | 7 +- src/Test/Perf/StackDepthTest/Program.cs | 3 +- .../BuildValidator/LocalSourceResolver.cs | 2 +- .../RazorLanguageServerFactoryWrapper.cs | 5 +- .../SerializationBenchmarks.cs | 3 +- .../Impl/CodeModel/CSharpCodeModelService.cs | 4 +- .../TempPECompilerService.cs | 7 +- .../Implementation/AbstractEditorFactory.cs | 10 +- .../PreviewUpdater.PreviewDialogWorkspace.cs | 7 +- .../MiscellaneousFilesWorkspace.cs | 5 +- ...tudioProject.BatchingDocumentCollection.cs | 43 +-- .../Def/ProjectSystem/VisualStudioProject.cs | 7 + .../VisualStudioProjectFactory.cs | 10 +- .../VisualStudioProjectOptionsProcessor.cs | 1 + ...sualStudioWorkspaceImpl.OpenFileTracker.cs | 15 +- .../Core/Def/Venus/ContainedLanguage.cs | 7 +- .../Impl/CodeModel/CodeModelProjectCache.cs | 5 +- .../CPSProject_IWorkspaceProjectContext.cs | 8 +- .../Services/SolutionServiceTests.cs | 2 + .../Projects/RemoteProjectInfoProvider.cs | 1 + .../RoslynRemoteProjectInfoProvider.cs | 33 +- .../WorkspaceFileTextLoaderNoException.cs | 8 +- .../Client/RemoteLanguageServiceWorkspace.cs | 15 +- .../CodeModel/VisualBasicCodeModelService.vb | 4 +- ...xTreeFactoryService.NodeSyntaxReference.cs | 35 ++ ...ntaxTreeFactoryService.ParsedSyntaxTree.cs | 83 ++++ ...reeFactoryService.RecoverableSyntaxTree.cs | 2 +- .../CSharpSyntaxTreeFactoryService.cs | 6 +- .../MSBuild/MSBuild/Constants/ItemNames.cs | 1 + .../MSBuild/Constants/PropertyNames.cs | 1 + .../MSBuild/MSBuildProjectLoader.Worker.cs | 58 ++- .../Core/MSBuild/MSBuild/MSBuildWorkspace.cs | 2 +- .../MSBuild/ProjectFile/ProjectFile.cs | 7 + .../MSBuild/ProjectFile/ProjectFileInfo.cs | 10 + .../Microsoft.CodeAnalysis.Workspaces.csproj | 1 + .../Serialization/SerializableSourceText.cs | 2 +- .../Shared/Extensions/SourceTextExtensions.cs | 4 +- .../TemporaryStorageServiceFactory.cs | 2 +- .../Core/Portable/Workspace/AdhocWorkspace.cs | 2 +- .../Portable/Workspace/CommandLineProject.cs | 28 +- ...ryService.AbstractRecoverableSyntaxRoot.cs | 4 +- .../AbstractSyntaxTreeFactoryService.cs | 4 +- .../ISyntaxTreeFactoryService.cs | 4 +- .../Host/TextFactory/ITextFactoryService.cs | 6 +- .../Host/TextFactory/TextFactoryService.cs | 16 +- .../Solution/AdditionalDocumentState.cs | 4 +- .../Solution/AnalyzerConfigDocumentState.cs | 4 +- .../Solution/ConstantTextAndVersionSource.cs | 31 ++ .../Workspace/Solution/DocumentInfo.cs | 80 ++-- .../Workspace/Solution/DocumentState.cs | 80 ++-- .../Solution/DocumentState_TreeTextSource.cs | 25 +- .../{ => Solution}/FileTextLoader.cs | 35 ++ .../Solution/ITextAndVersionSource.cs | 20 + .../Solution/LoadableTextAndVersionSource.cs | 69 ++++ .../Workspace/Solution/NullTextLoader.cs | 34 ++ .../Workspace/Solution/ProjectInfo.cs | 158 +++++--- .../Workspace/Solution/ProjectState.cs | 45 ++- .../Solution/RecoverableTextAndVersion.cs | 90 ++++- .../Portable/Workspace/Solution/Solution.cs | 135 ++++--- .../Workspace/Solution/SolutionState.cs | 33 ++ .../Solution/SourceGeneratedDocumentState.cs | 10 +- .../Workspace/Solution/TextDocumentState.cs | 41 +- .../Portable/Workspace/Solution/TextLoader.cs | 16 +- .../Workspace/Solution/TreeAndVersion.cs | 18 +- .../Core/Portable/Workspace/Workspace.cs | 32 +- .../Portable/Workspace/Workspace_Editor.cs | 6 + .../CoreTest/Simplifier/SimplifierTests.cs | 2 +- .../SolutionTests/DocumentInfoTests.cs | 27 +- .../SolutionTests/ProjectInfoTests.cs | 6 +- .../SolutionTests/SolutionTestHelpers.cs | 2 +- .../CoreTest/SolutionTests/SolutionTests.cs | 360 ++++++++++++++++-- .../SolutionWithSourceGeneratorTests.cs | 9 +- .../SourceTextSerializationTests.cs | 2 +- .../CoreTest/WorkspaceTests/WorkspaceTests.cs | 2 +- .../CoreTestUtilities/TestTextLoader.cs | 11 +- .../ProjectFiles/CSharp/AdditionalFile.csproj | 3 +- .../Utilities/VisualStudioMSBuildInstalled.cs | 17 +- .../VisualStudioMSBuildWorkspaceTests.cs | 34 ++ .../Remote/Core/AbstractAssetProvider.cs | 39 +- .../Host/RemoteWorkspace.SolutionCreator.cs | 49 +-- .../Compiler/Core/Utilities/AsyncLazy`1.cs | 2 + .../ValuesSources/ConstantValueSource.cs | 13 +- .../Core/Extensions/ProjectExtensions.cs | 14 - ...CodeAnalysis.VisualBasic.Workspaces.vbproj | 3 - ...xTreeFactoryService.NodeSyntaxReference.vb | 38 ++ ...ntaxTreeFactoryService.ParsedSyntaxTree.vb | 90 +++++ ...reeFactoryService.RecoverableSyntaxTree.vb | 2 +- .../VisualBasicSyntaxTreeFactoryService.vb | 6 +- 186 files changed, 2281 insertions(+), 990 deletions(-) rename src/Compilers/Core/Portable/{Debugging => Text}/SourceHashAlgorithms.cs (93%) create mode 100644 src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb create mode 100644 src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.NodeSyntaxReference.cs create mode 100644 src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs create mode 100644 src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs rename src/Workspaces/Core/Portable/Workspace/{ => Solution}/FileTextLoader.cs (87%) create mode 100644 src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs create mode 100644 src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs create mode 100644 src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs create mode 100644 src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.NodeSyntaxReference.vb create mode 100644 src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb diff --git a/eng/config/BannedSymbols.txt b/eng/config/BannedSymbols.txt index c3d693297ea10..5f783bc5534a6 100644 --- a/eng/config/BannedSymbols.txt +++ b/eng/config/BannedSymbols.txt @@ -35,4 +35,15 @@ M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAna M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.Text.TextSpan,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,System.Collections.Generic.IEnumerable{Microsoft.CodeAnalysis.Text.TextSpan},Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Editing.SyntaxEditor.#ctor(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.Host.HostWorkspaceServices); Use overload that takes HostSolutionServices instead -M:Microsoft.CodeAnalysis.FileTextLoader.#ctor(System.String,System.Text.Encoding); use WorkspaceFileTextLoader \ No newline at end of file +M:Microsoft.CodeAnalysis.FileTextLoader.#ctor(System.String,System.Text.Encoding); use WorkspaceFileTextLoader +M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm +M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken) +M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode); Use CSharpSyntaxTree.Create +M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.CSharp.CSharpParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken); Use API that takes SourceText +M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.CSharp.CSharpParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Nullable{System.Boolean},System.Threading.CancellationToken); Use API that takes SourceText +M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm +M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken); Use overload with SourceText +M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Threading.CancellationToken); Use overload with SourceText +M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode); Use VisualBasicSyntaxTree.Create +M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.Create(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic}); Use overload with checksum algorithm +M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Threading.CancellationToken); Use overload with SourceText diff --git a/eng/targets/Imports.targets b/eng/targets/Imports.targets index 0320ff6eb4dd0..5cf3ec3486218 100644 --- a/eng/targets/Imports.targets +++ b/eng/targets/Imports.targets @@ -175,8 +175,8 @@ - - + + diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index 034a0f560e022..ae1c48ddba022 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -106,7 +106,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar bool embedAllSourceFiles = false; bool resourcesOrModulesSpecified = false; Encoding? codepage = null; - var checksumAlgorithm = SourceHashAlgorithmUtils.DefaultContentHashAlgorithm; + var checksumAlgorithm = SourceHashAlgorithms.Default; var defines = ArrayBuilder.GetInstance(); List metadataReferences = new List(); List analyzers = new List(); diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 72a5aa6c898a5..e11a70d68043a 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -19,3 +19,9 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ScopedType(Microsoft.CodeAnal static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ScopedType(Microsoft.CodeAnalysis.SyntaxToken scopedKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type) -> Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> TResult? +*REMOVED*static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options = null, string? path = "", System.Text.Encoding? encoding = null) -> Microsoft.CodeAnalysis.SyntaxTree! +static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options = null, string? path = "", System.Text.Encoding? encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) -> Microsoft.CodeAnalysis.SyntaxTree! +static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options, string? path, System.Text.Encoding? encoding) -> Microsoft.CodeAnalysis.SyntaxTree! +*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options = null, string! path = "", System.Text.Encoding? encoding = null) -> Microsoft.CodeAnalysis.SyntaxTree! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options = null, string! path = "", System.Text.Encoding? encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) -> Microsoft.CodeAnalysis.SyntaxTree! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options, string! path, System.Text.Encoding? encoding) -> Microsoft.CodeAnalysis.SyntaxTree! diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs index ccfbd6e0e2117..8c121c0981826 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs @@ -72,7 +72,9 @@ private static SyntaxTree ComputeSyntaxTree(CSharpSyntaxNode node) if (parent == null) { // set the tree on the root node atomically +#pragma warning disable RS0030 // This method is intended to be used from this call site only Interlocked.CompareExchange(ref node._syntaxTree, CSharpSyntaxTree.CreateWithoutClone(node), null); +#pragma warning restore tree = node._syntaxTree; break; } diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs index cd8beaf8155fd..20b055565188f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs @@ -16,6 +16,8 @@ public partial class CSharpSyntaxTree { internal sealed class DummySyntaxTree : CSharpSyntaxTree { + private const SourceHashAlgorithm ChecksumAlgorithm = SourceHashAlgorithm.Sha1; + private readonly CompilationUnitSyntax _node; public DummySyntaxTree() @@ -30,12 +32,12 @@ public override string ToString() public override SourceText GetText(CancellationToken cancellationToken) { - return SourceText.From(string.Empty, Encoding.UTF8); + return SourceText.From(string.Empty, Encoding, ChecksumAlgorithm); } public override bool TryGetText(out SourceText text) { - text = SourceText.From(string.Empty, Encoding.UTF8); + text = SourceText.From(string.Empty, Encoding, ChecksumAlgorithm); return true; } @@ -90,14 +92,10 @@ public override bool HasCompilationUnitRoot } public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options) - { - return SyntaxFactory.SyntaxTree(root, options: options, path: FilePath, encoding: null); - } + => Create((CSharpSyntaxNode)root, (CSharpParseOptions)options, FilePath, Encoding); public override SyntaxTree WithFilePath(string path) - { - return SyntaxFactory.SyntaxTree(_node, options: this.Options, path: path, encoding: null); - } + => Create(_node, Options, path, Encoding); } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs index ae622d649cd75..1a158e7a5385f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs @@ -438,7 +438,7 @@ public static SyntaxTree ParseText( bool? isGeneratedCode, CancellationToken cancellationToken) { - return ParseText(SourceText.From(text, encoding), options, path, diagnosticOptions, isGeneratedCode, cancellationToken); + return ParseText(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), options, path, diagnosticOptions, isGeneratedCode, cancellationToken); } // The overload that has more parameters is itself obsolete, as an intentional break to allow future @@ -915,7 +915,7 @@ public static SyntaxTree ParseText( Encoding? encoding, ImmutableDictionary? diagnosticOptions, CancellationToken cancellationToken) - => ParseText(text, options, path, encoding, diagnosticOptions, isGeneratedCode: null, cancellationToken); + => ParseText(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), options, path, diagnosticOptions, isGeneratedCode: null, cancellationToken); // 3.3 BACK COMPAT OVERLOAD -- DO NOT MODIFY [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index a2e2e72a2d5b5..36dbc8e915dd2 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1550,7 +1550,7 @@ public static SyntaxTree SyntaxTree(SyntaxNode root, ParseOptions? options = nul #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters #pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. - +#pragma warning disable RS0030 // Do not used banned APIs /// public static SyntaxTree ParseSyntaxTree( string text, @@ -1561,6 +1561,7 @@ public static SyntaxTree ParseSyntaxTree( { return CSharpSyntaxTree.ParseText(text, (CSharpParseOptions?)options, path, encoding, cancellationToken); } +#pragma warning restore RS0030 // Do not used banned APIs /// public static SyntaxTree ParseSyntaxTree( diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index a40b5dcad2572..015a110838902 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -119,6 +119,32 @@ public void SourceGeneratedFiles() ", options: PdbValidationOptions.ExcludeMethods); } + [Fact] + public void EmitDebugInfoForSynthesizedSyntaxTree() + { + var tree1 = SyntaxFactory.ParseCompilationUnit(@" +#line 1 ""test.cs"" +class C { void M() {} } +").SyntaxTree; + var tree2 = SyntaxFactory.ParseCompilationUnit(@" +class D { void M() {} } +").SyntaxTree; + + var comp = CSharpCompilation.Create("test", new[] { tree1, tree2 }, TargetFrameworkUtil.StandardReferences, TestOptions.DebugDll); + + var result = comp.Emit(new MemoryStream(), pdbStream: new MemoryStream()); + result.Diagnostics.Verify(); + + comp.VerifyPdb(@" + + + + + + +", format: DebugInformationFormat.PortablePdb, options: PdbValidationOptions.ExcludeMethods); + } + [WorkItem(846584, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/846584")] [ConditionalFact(typeof(WindowsOnly))] public void RelativePathForExternalSource_Sha1_Windows() diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs index 6db23aec9d70e..9ea7328fb2aed 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -112,21 +113,27 @@ static SyntaxTree WithInitializedDirectives(SyntaxTree tree) } } - // Diagnostic options on syntax trees are now obsolete -#pragma warning disable CS0618 [Fact] + public void Create() + { + var root = SyntaxFactory.ParseCompilationUnit(""); + + var tree = CSharpSyntaxTree.Create(root); + Assert.Equal(SourceHashAlgorithm.Sha1, tree.GetText().ChecksumAlgorithm); + } + + // Diagnostic options on syntax trees are now obsolete + [Fact, Obsolete("Testing obsolete API")] public void Create_WithDiagnosticOptions() { var options = CreateImmutableDictionary(("CS0078", ReportDiagnostic.Suppress)); - var tree = CSharpSyntaxTree.Create(SyntaxFactory.ParseCompilationUnit(""), - options: null, - path: "", - encoding: null, - diagnosticOptions: options); + var tree = CSharpSyntaxTree.Create(SyntaxFactory.ParseCompilationUnit(""), options: null, path: null, encoding: null, diagnosticOptions: options); + Assert.Same(options, tree.DiagnosticOptions); + Assert.Equal(SourceHashAlgorithm.Sha1, tree.GetText().ChecksumAlgorithm); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void ParseTreeWithChangesPreservesDiagnosticOptions() { var options = CreateImmutableDictionary(("CS0078", ReportDiagnostic.Suppress)); @@ -142,7 +149,7 @@ public void ParseTreeWithChangesPreservesDiagnosticOptions() Assert.Same(options, newTree.DiagnosticOptions); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void ParseTreeNullDiagnosticOptions() { var tree = CSharpSyntaxTree.ParseText( @@ -158,7 +165,7 @@ public void ParseTreeNullDiagnosticOptions() Assert.NotSame(ImmutableDictionary.Empty, tree.DiagnosticOptions); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void ParseTreeEmptyDiagnosticOptions() { var tree = CSharpSyntaxTree.ParseText( @@ -173,7 +180,7 @@ public void ParseTreeEmptyDiagnosticOptions() Assert.Same(ImmutableDictionary.Empty, tree.DiagnosticOptions); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void ParseTreeCustomDiagnosticOptions() { var options = CreateImmutableDictionary(("CS0078", ReportDiagnostic.Suppress)); @@ -187,7 +194,7 @@ public void ParseTreeCustomDiagnosticOptions() Assert.Same(options, tree.DiagnosticOptions); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void DefaultTreeDiagnosticOptions() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -195,7 +202,7 @@ public void DefaultTreeDiagnosticOptions() Assert.True(tree.DiagnosticOptions.IsEmpty); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void WithDiagnosticOptionsNull() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -205,7 +212,7 @@ public void WithDiagnosticOptionsNull() Assert.Same(tree, newTree); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void WithDiagnosticOptionsEmpty() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -216,7 +223,7 @@ public void WithDiagnosticOptionsEmpty() Assert.NotSame(tree.DiagnosticOptions, newTree.DiagnosticOptions); } - [Fact] + [Fact, Obsolete("Testing obsolete API")] public void PerTreeDiagnosticOptionsNewDict() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -227,7 +234,6 @@ public void PerTreeDiagnosticOptionsNewDict() Assert.Same(map, newTree.DiagnosticOptions); Assert.NotEqual(tree, newTree); } -#pragma warning restore CS0618 [Fact] public void WithRootAndOptions_ParsedTree() @@ -248,7 +254,7 @@ public void WithRootAndOptions_ParsedTree() [Fact] public void WithRootAndOptions_ParsedTreeWithText() { - var oldText = SourceText.From("class B {}", Encoding.Unicode, SourceHashAlgorithm.Sha256); + var oldText = SourceText.From("class B {}", Encoding.Unicode, SourceHashAlgorithms.Default); var oldTree = SyntaxFactory.ParseSyntaxTree(oldText); var newRoot = SyntaxFactory.ParseCompilationUnit("class C {}"); @@ -259,7 +265,7 @@ public void WithRootAndOptions_ParsedTreeWithText() Assert.Equal(newRoot.ToString(), newTree.GetRoot().ToString()); Assert.Same(newOptions, newTree.Options); Assert.Same(Encoding.Unicode, newText.Encoding); - Assert.Equal(SourceHashAlgorithm.Sha256, newText.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, newText.ChecksumAlgorithm); } [Fact] @@ -290,7 +296,7 @@ public void WithFilePath_ParsedTree() [Fact] public void WithFilePath_ParsedTreeWithText() { - var oldText = SourceText.From("class B {}", Encoding.Unicode, SourceHashAlgorithm.Sha256); + var oldText = SourceText.From("class B {}", Encoding.Unicode, SourceHashAlgorithms.Default); var oldTree = SyntaxFactory.ParseSyntaxTree(oldText, path: "old.cs"); var newTree = oldTree.WithFilePath("new.cs"); @@ -300,7 +306,7 @@ public void WithFilePath_ParsedTreeWithText() Assert.Equal(oldTree.ToString(), newTree.ToString()); Assert.Same(Encoding.Unicode, newText.Encoding); - Assert.Equal(SourceHashAlgorithm.Sha256, newText.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, newText.ChecksumAlgorithm); } [Fact] @@ -321,7 +327,7 @@ public void WithFilePath_Null() oldTree = SyntaxFactory.ParseSyntaxTree("", path: "old.cs"); Assert.Equal(string.Empty, oldTree.WithFilePath(null).FilePath); Assert.Equal(string.Empty, SyntaxFactory.ParseSyntaxTree("", path: null).FilePath); - Assert.Equal(string.Empty, CSharpSyntaxTree.Create((CSharpSyntaxNode)oldTree.GetRoot(), path: null).FilePath); + Assert.Equal(string.Empty, CSharpSyntaxTree.Create((CSharpSyntaxNode)oldTree.GetRoot()).FilePath); } [Fact] diff --git a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs index 539fa4c8bcb6b..a599c96926ed3 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs @@ -10,6 +10,7 @@ using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -474,7 +475,7 @@ public void Initialize(GeneratorInitializationContext context) {{ }} var generatorPath = Path.Combine(directory.Path, $"generator_{targetFramework}.dll"); var compilation = CSharpCompilation.Create($"generator_{targetFramework}", - new[] { CSharpSyntaxTree.ParseText(generatorSource) }, + new[] { CSharpTestSource.Parse(generatorSource) }, TargetFrameworkUtil.GetReferences(TargetFramework.Standard, new[] { MetadataReference.CreateFromAssemblyInternal(typeof(ISourceGenerator).Assembly) }), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTests.cs index 89b6c5ce3a0c4..66fa105e1c92d 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTests.cs @@ -10,6 +10,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.FlowAnalysis; using Microsoft.CodeAnalysis.Operations; @@ -270,7 +271,7 @@ void M(int x) x = 0; } }"; - var tree = CSharpSyntaxTree.ParseText(source); + var tree = CSharpTestSource.Parse(source); var compilation = CSharpCompilation.Create("c", new[] { tree }); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: true); var methodBodySyntax = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Last(); diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeCompilerTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeCompilerTests.cs index 9edf099ef6ca4..0e2fe1aa7ba9a 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeCompilerTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeCompilerTests.cs @@ -11,10 +11,12 @@ using System.Runtime.ExceptionServices; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.UnitTests; using Roslyn.Test.Utilities; using Xunit; @@ -55,7 +57,7 @@ public UnconditionalSuppressMessageAttribute(string category, string checkId) }"; var compRef = CSharpCompilation.Create("unconditionalsuppress", options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary), - syntaxTrees: new[] { CSharpSyntaxTree.ParseText(unconditionalSuppressMessageDef) }, + syntaxTrees: new[] { CSharpTestSource.Parse(unconditionalSuppressMessageDef) }, references: new[] { TestBase.MscorlibRef }).EmitToImageReference(); return ImmutableArray.Create(TestBase.MscorlibRef, compRef, TestBase.ValueTupleRef); @@ -69,8 +71,8 @@ private static Compilation CreateCompilation(string source, string language, str var references = s_references.Value; var syntaxTree = language == LanguageNames.CSharp ? - CSharpSyntaxTree.ParseText(source, path: fileName) : - VisualBasicSyntaxTree.ParseText(source, path: fileName); + CSharpTestSource.Parse(source, path: fileName) : + BasicTestSource.Parse(source, path: fileName); if (language == LanguageNames.CSharp) { diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageTargetSymbolResolverTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageTargetSymbolResolverTests.cs index d4a11c5acd9b7..9db2805572de5 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageTargetSymbolResolverTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageTargetSymbolResolverTests.cs @@ -6,9 +6,11 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.UnitTests; using Roslyn.Test.Utilities; using Xunit; using System.Collections.Generic; @@ -1422,8 +1424,8 @@ private static SyntaxTree CreateSyntaxTree(string source, string language) string fileName = language == LanguageNames.CSharp ? "Test.cs" : "Test.vb"; return language == LanguageNames.CSharp ? - CSharpSyntaxTree.ParseText(source, path: fileName) : - VisualBasicSyntaxTree.ParseText(source, path: fileName); + CSharpTestSource.Parse(source, path: fileName) : + BasicTestSource.Parse(source, path: fileName); } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/EmbeddedTextTests.cs b/src/Compilers/Core/CodeAnalysisTest/EmbeddedTextTests.cs index a72360e7926dc..ff6c99e7f82b1 100644 --- a/src/Compilers/Core/CodeAnalysisTest/EmbeddedTextTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/EmbeddedTextTests.cs @@ -156,11 +156,11 @@ public void FromSource_Small() public void FromBytes_Large() { var bytes = Encoding.Unicode.GetBytes(LargeSource); - var checksum = SourceText.CalculateChecksum(bytes, 0, bytes.Length, SourceHashAlgorithm.Sha256); - var text = EmbeddedText.FromBytes("pathToLarge", new ArraySegment(bytes, 0, bytes.Length), SourceHashAlgorithm.Sha256); + var checksum = SourceText.CalculateChecksum(bytes, 0, bytes.Length, SourceHashAlgorithms.Default); + var text = EmbeddedText.FromBytes("pathToLarge", new ArraySegment(bytes, 0, bytes.Length), SourceHashAlgorithms.Default); Assert.Equal("pathToLarge", text.FilePath); - Assert.Equal(SourceHashAlgorithm.Sha256, text.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, text.ChecksumAlgorithm); AssertEx.Equal(checksum, text.Checksum); AssertEx.Equal(BitConverter.GetBytes(bytes.Length), text.Blob.Take(4)); AssertEx.Equal(bytes, Decompress(text.Blob.Skip(4))); @@ -171,12 +171,12 @@ public void FromBytes_LargeSpan() { var bytes = Encoding.Unicode.GetBytes(LargeSource); var paddedBytes = new byte[] { 0 }.Concat(bytes).Concat(new byte[] { 0 }).ToArray(); - var checksum = SourceText.CalculateChecksum(bytes, 0, bytes.Length, SourceHashAlgorithm.Sha256); - var text = EmbeddedText.FromBytes("pathToLarge", new ArraySegment(paddedBytes, 1, bytes.Length), SourceHashAlgorithm.Sha256); + var checksum = SourceText.CalculateChecksum(bytes, 0, bytes.Length, SourceHashAlgorithms.Default); + var text = EmbeddedText.FromBytes("pathToLarge", new ArraySegment(paddedBytes, 1, bytes.Length), SourceHashAlgorithms.Default); Assert.Equal("pathToLarge", text.FilePath); AssertEx.Equal(checksum, text.Checksum); - Assert.Equal(SourceHashAlgorithm.Sha256, text.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, text.ChecksumAlgorithm); AssertEx.Equal(BitConverter.GetBytes(bytes.Length), text.Blob.Take(4)); AssertEx.Equal(bytes, Decompress(text.Blob.Skip(4))); } @@ -184,11 +184,11 @@ public void FromBytes_LargeSpan() [Fact] public void FromSource_Large() { - var source = SourceText.From(LargeSource, Encoding.Unicode, SourceHashAlgorithm.Sha256); + var source = SourceText.From(LargeSource, Encoding.Unicode, SourceHashAlgorithms.Default); var text = EmbeddedText.FromSource("pathToLarge", source); Assert.Equal("pathToLarge", text.FilePath); - Assert.Equal(SourceHashAlgorithm.Sha256, text.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, text.ChecksumAlgorithm); AssertEx.Equal(source.GetChecksum(), text.Checksum); AssertEx.Equal(BitConverter.GetBytes(Encoding.Unicode.GetPreamble().Length + LargeSource.Length * sizeof(char)), text.Blob.Take(4)); AssertEx.Equal(Encoding.Unicode.GetPreamble().Concat(Encoding.Unicode.GetBytes(LargeSource)), Decompress(text.Blob.Skip(4))); diff --git a/src/Compilers/Core/CodeAnalysisTest/GivesAccessTo.cs b/src/Compilers/Core/CodeAnalysisTest/GivesAccessTo.cs index 928e3e3634f01..4890efa8980e7 100644 --- a/src/Compilers/Core/CodeAnalysisTest/GivesAccessTo.cs +++ b/src/Compilers/Core/CodeAnalysisTest/GivesAccessTo.cs @@ -6,7 +6,9 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.UnitTests; using Roslyn.Test.Utilities; using Xunit; @@ -17,25 +19,25 @@ public class GivesAccessTo [Fact, WorkItem(26459, "https://github.com/dotnet/roslyn/issues/26459")] public void TestGivesAccessTo_CrossLanguageAndCompilation() { - var csharpTree = CSharpSyntaxTree.ParseText(@" + var csharpTree = CSharpTestSource.Parse(@" [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""VB"")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""CS2"")] internal class CS { } "); - var csharpTree2 = CSharpSyntaxTree.ParseText(@" + var csharpTree2 = CSharpTestSource.Parse(@" internal class CS2 { } "); - var vbTree = VisualBasicSyntaxTree.ParseText(@" + var vbTree = BasicTestSource.Parse(@" Friend Class VB End Class "); - var vbTree2 = VisualBasicSyntaxTree.ParseText(@" + var vbTree2 = BasicTestSource.Parse(@" Friend Class VB2 End Class "); diff --git a/src/Compilers/Core/CodeAnalysisTest/IsSymbolAccessibleWithinTests.cs b/src/Compilers/Core/CodeAnalysisTest/IsSymbolAccessibleWithinTests.cs index cf449700e4f29..c140633cede84 100644 --- a/src/Compilers/Core/CodeAnalysisTest/IsSymbolAccessibleWithinTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/IsSymbolAccessibleWithinTests.cs @@ -7,7 +7,9 @@ using System; using System.Linq; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.UnitTests; using Roslyn.Test.Utilities; using Xunit; @@ -18,8 +20,8 @@ public class IsSymbolAccessibleWithinTests [Fact] public void CrossLanguageException() { - var csharpTree = CSharpSyntaxTree.ParseText("class A { }"); - var vbTree = VisualBasicSyntaxTree.ParseText( + var csharpTree = CSharpTestSource.Parse("class A { }"); + var vbTree = BasicTestSource.Parse( @"Class A End Class "); diff --git a/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/MetadataReferenceTests.cs b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/MetadataReferenceTests.cs index 97ee1e1246445..4b7d1c3adb421 100644 --- a/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/MetadataReferenceTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/MetadataReferenceTests.cs @@ -13,6 +13,7 @@ using System.Reflection; using System.Threading; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Symbols; using Microsoft.CodeAnalysis.VisualBasic; using Roslyn.Test.Utilities; @@ -42,7 +43,7 @@ public void CreateFromAssembly_NoMetadata() public void CreateFrom_Errors() { Assert.Throws(() => MetadataReference.CreateFromImage(null)); - Assert.Throws(() => MetadataReference.CreateFromImage(default(ImmutableArray))); + Assert.Throws(() => MetadataReference.CreateFromImage(default)); Assert.Throws(() => MetadataReference.CreateFromFile(null)); Assert.Throws(() => MetadataReference.CreateFromFile(null, default(MetadataReferenceProperties))); Assert.Throws(() => MetadataReference.CreateFromStream(null)); @@ -174,7 +175,7 @@ public void CreateFromAssembly_WithPropertiesAndDocumentation() private class TestDocumentationProvider : DocumentationProvider { - protected internal override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default(CancellationToken)) + protected internal override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default) { return string.Format("{0}", documentationMemberID); } @@ -405,7 +406,7 @@ private class MyReference : PortableExecutableReference private readonly string _display; public MyReference(string fullPath, string display) - : base(default(MetadataReferenceProperties), fullPath) + : base(default, fullPath) { _display = display; } @@ -433,8 +434,8 @@ protected override PortableExecutableReference WithPropertiesImpl(MetadataRefere private class MyReference2 : PortableExecutableReference { - public MyReference2(string fullPath, string display) - : base(default(MetadataReferenceProperties), fullPath) + public MyReference2(string fullPath) + : base(properties: default, fullPath) { } @@ -476,8 +477,6 @@ public void Equivalence() Assert.Equal("m1b", m1b.Display); var m2 = new MyReference(@"c:\b\goo.dll", display: "m2"); Assert.Equal("m2", m2.Display); - var m3 = new MyReference(null, display: "m3"); - var m4 = new MyReference(null, display: "m4"); var c1a = CS.CSharpCompilation.Create("goo").ToMetadataReference(); var c1b = c1a.Compilation.ToMetadataReference(); @@ -504,19 +503,6 @@ public void Equivalence() } } - [Fact] - public void PortableReference_Display() - { - var comparer = CommonReferenceManager.MetadataReferenceEqualityComparer.Instance; - - var f1 = MscorlibRef; - var f2 = SystemCoreRef; - - var m1a = new MyReference2(@"c:\a\goo.dll", display: "m1a"); - Assert.Equal(@"c:\a\goo.dll", m1a.Display); - Assert.Equal(@"c:\a\goo.dll", m1a.FilePath); - } - [Fact] public void DocCommentProvider() { @@ -525,7 +511,7 @@ public void DocCommentProvider() GetReference(display: "corlib", documentation: docProvider); var comp = (Compilation)CS.CSharpCompilation.Create("goo", - syntaxTrees: new[] { CS.SyntaxFactory.ParseSyntaxTree("class C : System.Collections.ArrayList { }") }, + syntaxTrees: new[] { CSharpTestSource.Parse("class C : System.Collections.ArrayList { }") }, references: new[] { corlib }); var c = (ITypeSymbol)comp.GlobalNamespace.GetMembers("C").Single(); diff --git a/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj b/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj index a43e46d8035cb..3420d346d8835 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj +++ b/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj @@ -19,6 +19,8 @@ + + diff --git a/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextTests.cs b/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextTests.cs index 0454eeb83c933..27f1445fb3fa7 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextTests.cs @@ -70,21 +70,29 @@ public void EncodingBOM() } [Fact] - public void ChecksumAlgorithm1() + public void ChecksumAlgorithm_Default() { Assert.Equal(SourceHashAlgorithm.Sha1, SourceText.From(HelloWorld).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, SourceText.From(HelloWorld, checksumAlgorithm: SourceHashAlgorithm.Sha1).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha256, SourceText.From(HelloWorld, checksumAlgorithm: SourceHashAlgorithm.Sha256).ChecksumAlgorithm); var bytes = s_unicode.GetBytes(HelloWorld); Assert.Equal(SourceHashAlgorithm.Sha1, SourceText.From(bytes, bytes.Length).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, SourceText.From(bytes, bytes.Length, checksumAlgorithm: SourceHashAlgorithm.Sha1).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha256, SourceText.From(bytes, bytes.Length, checksumAlgorithm: SourceHashAlgorithm.Sha256).ChecksumAlgorithm); var stream = new MemoryStream(bytes); Assert.Equal(SourceHashAlgorithm.Sha1, SourceText.From(stream).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, SourceText.From(stream, checksumAlgorithm: SourceHashAlgorithm.Sha1).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha256, SourceText.From(stream, checksumAlgorithm: SourceHashAlgorithm.Sha256).ChecksumAlgorithm); + } + + [Theory] + [InlineData(SourceHashAlgorithm.Sha1)] + [InlineData(SourceHashAlgorithm.Sha256)] + public void ChecksumAlgorithm1(SourceHashAlgorithm algorithm) + { + Assert.Equal(algorithm, SourceText.From(HelloWorld, checksumAlgorithm: algorithm).ChecksumAlgorithm); + + var bytes = s_unicode.GetBytes(HelloWorld); + Assert.Equal(algorithm, SourceText.From(bytes, bytes.Length, checksumAlgorithm: algorithm).ChecksumAlgorithm); + + var stream = new MemoryStream(bytes); + Assert.Equal(algorithm, SourceText.From(stream, checksumAlgorithm: algorithm).ChecksumAlgorithm); } [WorkItem(7225, "https://github.com/dotnet/roslyn/issues/7225")] diff --git a/src/Compilers/Core/CodeAnalysisTest/Text/TextChangeTests.cs b/src/Compilers/Core/CodeAnalysisTest/Text/TextChangeTests.cs index 0c44b156fa210..995b84fd21f61 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Text/TextChangeTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Text/TextChangeTests.cs @@ -186,7 +186,7 @@ public void TestChangedTextWithDeleteAfterDeleteAdjacent() [Fact] public void TestSubTextAfterMultipleChanges() { - var text = SourceText.From("Hello World", Encoding.Unicode, SourceHashAlgorithm.Sha256); + var text = SourceText.From("Hello World", Encoding.Unicode, SourceHashAlgorithms.Default); var newText = text.WithChanges( new TextChange(new TextSpan(4, 1), string.Empty), new TextChange(new TextSpan(6, 5), "Universe")); @@ -194,7 +194,7 @@ public void TestSubTextAfterMultipleChanges() var subText = newText.GetSubText(new TextSpan(3, 4)); Assert.Equal("l Un", subText.ToString()); - Assert.Equal(SourceHashAlgorithm.Sha256, subText.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, subText.ChecksumAlgorithm); Assert.Same(Encoding.Unicode, subText.Encoding); } @@ -228,7 +228,7 @@ public void TestCopyTo() [Fact] public void TestGetTextChangesToChangedText() { - var text = SourceText.From(new string('.', 2048), Encoding.Unicode, SourceHashAlgorithm.Sha256); // start bigger than GetText() copy buffer + var text = SourceText.From(new string('.', 2048), Encoding.Unicode, SourceHashAlgorithms.Default); // start bigger than GetText() copy buffer var changes = new TextChange[] { new TextChange(new TextSpan(0, 1), "[1]"), new TextChange(new TextSpan(1, 1), "[2]"), @@ -237,7 +237,7 @@ public void TestGetTextChangesToChangedText() }; var newText = text.WithChanges(changes); - Assert.Equal(SourceHashAlgorithm.Sha256, newText.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, newText.ChecksumAlgorithm); Assert.Same(Encoding.Unicode, newText.Encoding); var result = newText.GetTextChanges(text).ToList(); @@ -587,7 +587,7 @@ public void TestLargeTextWriterReusesLargeChunks() private SourceText CreateLargeText(params char[][] chunks) { - return new LargeText(ImmutableArray.Create(chunks), Encoding.UTF8, default(ImmutableArray), SourceHashAlgorithm.Sha256, default(ImmutableArray)); + return new LargeText(ImmutableArray.Create(chunks), Encoding.UTF8, default(ImmutableArray), SourceHashAlgorithms.Default, default(ImmutableArray)); } private ImmutableArray GetChunks(SourceText text) diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets index 1cb4a18886d00..a0c1d19c8d26f 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets @@ -184,7 +184,7 @@ probably won't be any worse than having no options at all. --> - -langversion:$(LangVersion) + -langversion:$(LangVersion) -checksumalgorithm:$(ChecksumAlgorithm) $(CommandLineArgsForDesignTimeEvaluation) -define:$(DefineConstants) \ No newline at end of file diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets index e88164ac42f35..80a21c5e6e5f7 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.VisualBasic.Core.targets @@ -153,7 +153,7 @@ probably won't be any worse than having no options at all. --> - -langversion:$(LangVersion) + -langversion:$(LangVersion) -checksumalgorithm:$(ChecksumAlgorithm) $(CommandLineArgsForDesignTimeEvaluation) -define:$(FinalDefineConstants) diff --git a/src/Compilers/Core/Portable/CryptographicHashProvider.cs b/src/Compilers/Core/Portable/CryptographicHashProvider.cs index 66882850f2777..a4534494ff92b 100644 --- a/src/Compilers/Core/Portable/CryptographicHashProvider.cs +++ b/src/Compilers/Core/Portable/CryptographicHashProvider.cs @@ -203,7 +203,7 @@ internal static ImmutableArray ComputeHash(HashAlgorithmName algorithmName } } - internal static ImmutableArray ComputeSourceHash(ImmutableArray bytes, SourceHashAlgorithm hashAlgorithm = SourceHashAlgorithmUtils.DefaultContentHashAlgorithm) + internal static ImmutableArray ComputeSourceHash(ImmutableArray bytes, SourceHashAlgorithm hashAlgorithm = SourceHashAlgorithms.Default) { var algorithmName = GetAlgorithmName(hashAlgorithm); using (var incrementalHash = IncrementalHash.CreateHash(algorithmName)) @@ -213,7 +213,7 @@ internal static ImmutableArray ComputeSourceHash(ImmutableArray byte } } - internal static ImmutableArray ComputeSourceHash(IEnumerable bytes, SourceHashAlgorithm hashAlgorithm = SourceHashAlgorithmUtils.DefaultContentHashAlgorithm) + internal static ImmutableArray ComputeSourceHash(IEnumerable bytes, SourceHashAlgorithm hashAlgorithm = SourceHashAlgorithms.Default) { return ComputeHash(GetAlgorithmName(hashAlgorithm), bytes); } diff --git a/src/Compilers/Core/Portable/EmbeddedText.cs b/src/Compilers/Core/Portable/EmbeddedText.cs index 45910e8cd7ea3..5495de2bf9d6d 100644 --- a/src/Compilers/Core/Portable/EmbeddedText.cs +++ b/src/Compilers/Core/Portable/EmbeddedText.cs @@ -10,7 +10,6 @@ using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj index 9cfc181b99223..090dcf544b8b9 100644 --- a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj @@ -83,6 +83,7 @@ + diff --git a/src/Compilers/Core/Portable/PEWriter/DebugSourceInfo.cs b/src/Compilers/Core/Portable/PEWriter/DebugSourceInfo.cs index 5d455299e8844..61533c679455e 100644 --- a/src/Compilers/Core/Portable/PEWriter/DebugSourceInfo.cs +++ b/src/Compilers/Core/Portable/PEWriter/DebugSourceInfo.cs @@ -2,7 +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. -using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Text; using System; using System.Collections.Immutable; diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index e26e7f61f374f..a7044eb538863 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -71,8 +71,7 @@ private string GetDebuggerDisplay() internal int EndPosition => Position + Green.FullWidth; /// - /// Returns SyntaxTree that owns the node or null if node does not belong to a - /// SyntaxTree + /// Returns that owns the node. /// public SyntaxTree SyntaxTree => this.SyntaxTreeCore; diff --git a/src/Compilers/Core/Portable/Text/SourceHashAlgorithm.cs b/src/Compilers/Core/Portable/Text/SourceHashAlgorithm.cs index e0c1c3bcb7bd1..cdcc18b9ec3f1 100644 --- a/src/Compilers/Core/Portable/Text/SourceHashAlgorithm.cs +++ b/src/Compilers/Core/Portable/Text/SourceHashAlgorithm.cs @@ -24,9 +24,4 @@ public enum SourceHashAlgorithm /// Sha256 = 2, } - - internal static class SourceHashAlgorithmUtils - { - public const SourceHashAlgorithm DefaultContentHashAlgorithm = SourceHashAlgorithm.Sha256; - } } diff --git a/src/Compilers/Core/Portable/Debugging/SourceHashAlgorithms.cs b/src/Compilers/Core/Portable/Text/SourceHashAlgorithms.cs similarity index 93% rename from src/Compilers/Core/Portable/Debugging/SourceHashAlgorithms.cs rename to src/Compilers/Core/Portable/Text/SourceHashAlgorithms.cs index 32f2bfe281697..889c3216aa883 100644 --- a/src/Compilers/Core/Portable/Debugging/SourceHashAlgorithms.cs +++ b/src/Compilers/Core/Portable/Text/SourceHashAlgorithms.cs @@ -2,19 +2,18 @@ // 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 Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Debugging +namespace Microsoft.CodeAnalysis.Text { /// /// Hash algorithms supported by the debugger used for source file checksums stored in the PDB. /// internal static class SourceHashAlgorithms { + public const SourceHashAlgorithm Default = SourceHashAlgorithm.Sha256; + private static readonly Guid s_guidSha1 = unchecked(new Guid((int)0xff1816ec, (short)0xaa5e, 0x4d10, 0x87, 0xf7, 0x6f, 0x49, 0x63, 0x83, 0x34, 0x60)); private static readonly Guid s_guidSha256 = unchecked(new Guid((int)0x8829d00f, 0x11b8, 0x4213, 0x87, 0x8b, 0x77, 0x0e, 0x85, 0x97, 0xac, 0x16)); diff --git a/src/Compilers/Core/Portable/Text/SourceText.cs b/src/Compilers/Core/Portable/Text/SourceText.cs index 0f970dd0f3134..b9fefa4ffd17f 100644 --- a/src/Compilers/Core/Portable/Text/SourceText.cs +++ b/src/Compilers/Core/Portable/Text/SourceText.cs @@ -12,7 +12,6 @@ using System.Linq; using System.Text; using System.Threading; -using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; diff --git a/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs b/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs index 248377e55db51..e528128b9257e 100644 --- a/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs +++ b/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs @@ -80,7 +80,7 @@ static void verifyCount(int expected) public void Simple() { var compilation = CSharpTestBase.CreateCompilation( - @"System.Console.WriteLine(""Hello World"");", + CSharpTestSource.Parse(@"System.Console.WriteLine(""Hello World"");", checksumAlgorithm: SourceHashAlgorithm.Sha1), targetFramework: TargetFramework.NetCoreApp, options: Options); @@ -375,7 +375,7 @@ void assert(string expected, params string[] values) [InlineData(@"e:\long\path\src\code.cs", @"e:\long\path\src\", @"/pathmap:e:\long\path\=c:\")] public void CSharpPathMapWindows(string filePath, string workingDirectory, string? pathMap) { - var args = new List(new[] { filePath, "/nostdlib", "/langversion:9" }); + var args = new List(new[] { filePath, "/nostdlib", "/langversion:9", "/checksumalgorithm:sha256" }); if (pathMap is not null) { args.Add(pathMap); @@ -497,7 +497,7 @@ public void MetadataReferenceCompilation() [Fact] public void FeatureFlag() { - var compiler = TestableCompiler.CreateCSharpNetCoreApp("test.cs", @"-t:library", "-nologo", "-features:debug-determinism", "-deterministic", "-debug:portable"); + var compiler = TestableCompiler.CreateCSharpNetCoreApp("test.cs", @"-t:library", "-nologo", "-features:debug-determinism", "-deterministic", "-debug:portable", "-checksumalgorithm:sha256"); var sourceFile = compiler.AddSourceFile("test.cs", @"// this is a test file"); compiler.AddOutputFile("test.dll"); var pdbFile = compiler.AddOutputFile("test.pdb"); diff --git a/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs b/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs index b7014a8ebb516..47344c21f5506 100644 --- a/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs @@ -14,6 +14,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CommandLine; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -113,7 +115,7 @@ public void NetstandardIgnored() const string name = "netstandardRef"; var comp = CSharpCompilation.Create( name, - new[] { SyntaxFactory.ParseSyntaxTree(@"class C {}") }, + new[] { CSharpTestSource.Parse("class C {}") }, references: new MetadataReference[] { NetStandard20.netstandard }, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, warningLevel: Diagnostic.MaxWarningLevel)); var compFile = directory.CreateFile(name); diff --git a/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj b/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj index 8ee6e9160de67..80868f5838360 100644 --- a/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj +++ b/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj @@ -18,6 +18,7 @@ csc + vbc diff --git a/src/Compilers/Test/Core/AssemblyLoadTestFixture.cs b/src/Compilers/Test/Core/AssemblyLoadTestFixture.cs index a01a348aec339..80ab0fb22d3be 100644 --- a/src/Compilers/Test/Core/AssemblyLoadTestFixture.cs +++ b/src/Compilers/Test/Core/AssemblyLoadTestFixture.cs @@ -8,6 +8,7 @@ using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -465,7 +466,7 @@ private static TempFile GenerateDll(string assemblyName, TempDirectory directory var analyzerDependencyCompilation = CSharpCompilation.Create( assemblyName: assemblyName, - syntaxTrees: new SyntaxTree[] { SyntaxFactory.ParseSyntaxTree(csSource) }, + syntaxTrees: new SyntaxTree[] { SyntaxFactory.ParseSyntaxTree(SourceText.From(csSource, encoding: null, SourceHashAlgorithms.Default)) }, references: (new MetadataReference[] { NetStandard20.mscorlib, diff --git a/src/Compilers/Test/Core/CommonTestBase.cs b/src/Compilers/Test/Core/CommonTestBase.cs index df25bfa36e7a3..fbe2f57c5f0bf 100644 --- a/src/Compilers/Test/Core/CommonTestBase.cs +++ b/src/Compilers/Test/Core/CommonTestBase.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -333,7 +334,7 @@ protected CSharp.CSharpCompilation CreateCSharpCompilation( AddReferencedCompilations(referencedCompilations, references); - var tree = CSharp.SyntaxFactory.ParseSyntaxTree(code, options: parseOptions); + var tree = CSharp.SyntaxFactory.ParseSyntaxTree(SourceText.From(code, encoding: null, SourceHashAlgorithms.Default), options: parseOptions); return CSharp.CSharpCompilation.Create(assemblyName, new[] { tree }, references, compilationOptions); } @@ -416,7 +417,7 @@ protected VisualBasic.VisualBasicCompilation CreateVisualBasicCompilation( var trees = new SyntaxTree[files.Length]; for (int i = 0; i < files.Length; i++) { - trees[i] = VisualBasic.VisualBasicSyntaxTree.ParseText(files[i], options: parseOptions, encoding: encoding, path: sourceFileNames?[i]); + trees[i] = VisualBasic.VisualBasicSyntaxTree.ParseText(SourceText.From(files[i], encoding, SourceHashAlgorithms.Default), options: parseOptions, path: sourceFileNames?[i]); } diff --git a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs index b09de6ec1964e..bef40eec7af14 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs @@ -23,6 +23,7 @@ using Microsoft.CodeAnalysis.FlowAnalysis; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -432,7 +433,7 @@ public static void RemoveEventHandler(Action removeMe // WellKnownTypes and WellKnownMembers so it can be safely skipped here. var compilation = CSharpCompilation.Create( "System.Runtime.InteropServices.WindowsRuntime", - new[] { CSharpSyntaxTree.ParseText(source) }, + new[] { CSharpSyntaxTree.ParseText(SourceText.From(source, encoding: null, SourceHashAlgorithms.Default)) }, references: TargetFrameworkUtil.GetReferences(TargetFramework.NetCoreApp), options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); compilation.VerifyEmitDiagnostics(); diff --git a/src/Compilers/Test/Core/Platform/Desktop/TestHelpers.cs b/src/Compilers/Test/Core/Platform/Desktop/TestHelpers.cs index ca24d0e79ea41..fc864457cf320 100644 --- a/src/Compilers/Test/Core/Platform/Desktop/TestHelpers.cs +++ b/src/Compilers/Test/Core/Platform/Desktop/TestHelpers.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Resources.Proprietary; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Microsoft.Win32; using Basic.Reference.Assemblies; @@ -79,7 +80,7 @@ public class TestAnalyzer : DiagnosticAnalyzer var analyzerCompilation = CSharpCompilation.Create( assemblyName, - new SyntaxTree[] { SyntaxFactory.ParseSyntaxTree(analyzerSource) }, + new SyntaxTree[] { SyntaxFactory.ParseSyntaxTree(SourceText.From(analyzerSource, encoding: null, SourceHashAlgorithms.Default)) }, new MetadataReference[] { NetStandard20.mscorlib, diff --git a/src/Compilers/Test/Core/TestBase.cs b/src/Compilers/Test/Core/TestBase.cs index 5101ed2d036e7..07ff54051988b 100644 --- a/src/Compilers/Test/Core/TestBase.cs +++ b/src/Compilers/Test/Core/TestBase.cs @@ -177,7 +177,7 @@ public virtual void Dispose() () => { var source = TestResources.NetFX.aacorlib_v15_0_3928.aacorlib_v15_0_3928_cs; - var syntaxTree = Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(source); + var syntaxTree = Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(SourceText.From(source, encoding: null, SourceHashAlgorithms.Default)); var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); diff --git a/src/Compilers/Test/Utilities/CSharp/BasicCompilationUtils.cs b/src/Compilers/Test/Utilities/CSharp/BasicCompilationUtils.cs index 469a80f4ea595..6c2c070e742ba 100644 --- a/src/Compilers/Test/Utilities/CSharp/BasicCompilationUtils.cs +++ b/src/Compilers/Test/Utilities/CSharp/BasicCompilationUtils.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.VisualBasic; using Roslyn.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using static Microsoft.CodeAnalysis.CodeGen.CompilationTestData; namespace Microsoft.CodeAnalysis.CSharp.UnitTests @@ -32,7 +33,7 @@ private static VisualBasicCompilation CreateCompilationWithMscorlib(string sourc { assemblyName = TestBase.GetUniqueName(); } - var tree = VisualBasicSyntaxTree.ParseText(source); + var tree = VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding: null, SourceHashAlgorithms.Default)); var options = new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release); return VisualBasicCompilation.Create(assemblyName, new[] { tree }, references, options); } diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 8cf2b81fdd2b6..f05474fe369fb 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -994,25 +994,7 @@ internal CompilationVerifier CompileAndVerifyFieldMarshal(CSharpTestSource sourc #region SyntaxTree Factories public static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null, Encoding encoding = null, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) - { - if ((object)options == null) - { - options = TestOptions.RegularPreview; - } - - var stringText = StringText.From(text, encoding ?? Encoding.UTF8, checksumAlgorithm); - return CheckSerializable(SyntaxFactory.ParseSyntaxTree(stringText, options, filename)); - } - - private static SyntaxTree CheckSerializable(SyntaxTree tree) - { - var stream = new MemoryStream(); - var root = tree.GetRoot(); - root.SerializeTo(stream); - stream.Position = 0; - var deserializedRoot = CSharpSyntaxNode.DeserializeFrom(stream); - return tree; - } + => CSharpTestSource.Parse(text, filename, options, encoding, checksumAlgorithm); public static SyntaxTree[] Parse(IEnumerable sources, CSharpParseOptions options = null) { diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs index bbd6b765e3393..0a910109b1e6f 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs @@ -8,7 +8,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.IO; using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities { @@ -27,12 +30,34 @@ private CSharpTestSource(object value) Value = value; } + public static SyntaxTree Parse( + string text, + string path = "", + CSharpParseOptions options = null, + Encoding encoding = null, + SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default) + { + var stringText = SourceText.From(text, encoding ?? Encoding.UTF8, checksumAlgorithm); + var tree = SyntaxFactory.ParseSyntaxTree(stringText, options ?? TestOptions.RegularPreview, path); + CheckSerializable(tree); + return tree; + } + + private static void CheckSerializable(SyntaxTree tree) + { + using var stream = new MemoryStream(); + var root = tree.GetRoot(); + root.SerializeTo(stream); + stream.Position = 0; + CSharpSyntaxNode.DeserializeFrom(stream); + } + public SyntaxTree[] GetSyntaxTrees(CSharpParseOptions parseOptions, string sourceFileName = "") { switch (Value) { case string source: - return new[] { CSharpTestBase.Parse(source, filename: sourceFileName, parseOptions) }; + return new[] { Parse(source, path: sourceFileName, parseOptions) }; case string[] sources: Debug.Assert(string.IsNullOrEmpty(sourceFileName)); return CSharpTestBase.Parse(parseOptions, sources); @@ -41,7 +66,7 @@ public SyntaxTree[] GetSyntaxTrees(CSharpParseOptions parseOptions, string sourc return new[] { CSharpTestBase.Parse(source, fileName, parseOptions) }; case (string Source, string FileName)[] sources: Debug.Assert(string.IsNullOrEmpty(sourceFileName)); - return sources.Select(source => CSharpTestBase.Parse(source.Source, source.FileName, parseOptions)).ToArray(); + return sources.Select(source => Parse(source.Source, source.FileName, parseOptions)).ToArray(); case SyntaxTree tree: Debug.Assert(parseOptions == null); Debug.Assert(string.IsNullOrEmpty(sourceFileName)); diff --git a/src/Compilers/Test/Utilities/CSharp/DiagnosticTestUtilities.cs b/src/Compilers/Test/Utilities/CSharp/DiagnosticTestUtilities.cs index 586c80e12437c..25442656faacb 100644 --- a/src/Compilers/Test/Utilities/CSharp/DiagnosticTestUtilities.cs +++ b/src/Compilers/Test/Utilities/CSharp/DiagnosticTestUtilities.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Xunit; @@ -63,7 +64,7 @@ protected internal static CSharpCompilation VerifyErrorsAndGetCompilationWithMsc protected internal static CSharpCompilation VerifyErrorsAndGetCompilationWithMscorlib(List srcs, IEnumerable refs, params ErrorDescription[] expectedErrorDesp) { var synTrees = (from text in srcs - select SyntaxFactory.ParseSyntaxTree(text)).ToArray(); + select SyntaxFactory.ParseSyntaxTree(SourceText.From(text, encoding: null, SourceHashAlgorithms.Default))).ToArray(); return VerifyErrorsAndGetCompilationWithMscorlib(synTrees, refs, expectedErrorDesp); } diff --git a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb index d235a7f26dc06..0f0381e96e4f6 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb @@ -3,7 +3,9 @@ ' See the LICENSE file in the project root for more information. Imports System.Xml.Linq +Imports System.Text Imports Microsoft.CodeAnalysis.Text +Imports System.IO Public Structure BasicTestSource @@ -13,6 +15,27 @@ Public Structure BasicTestSource Me.Value = value End Sub + Public Shared Function Parse(text As String, + Optional path As String = "", + Optional options As VisualBasicParseOptions = Nothing, + Optional encoding As Encoding = Nothing, + Optional checksumAlgorithm As SourceHashAlgorithm = SourceHashAlgorithms.Default) As SyntaxTree + + Dim sourceTest = SourceText.From(text, If(encoding, Encoding.UTF8), checksumAlgorithm) + Dim tree = SyntaxFactory.ParseSyntaxTree(sourceTest, If(options, TestOptions.RegularLatest), path) + CheckSerializable(tree) + Return tree + End Function + + Private Shared Sub CheckSerializable(tree As SyntaxTree) + Using stream = New MemoryStream() + Dim root = tree.GetRoot() + root.SerializeTo(stream) + stream.Position = 0 + VisualBasicSyntaxNode.DeserializeFrom(stream) + End Using + End Sub + Public Function GetSyntaxTrees( Optional parseOptions As VisualBasicParseOptions = Nothing, Optional ByRef assemblyName As String = Nothing, @@ -30,12 +53,12 @@ Public Structure BasicTestSource Dim source = TryCast(Value, String) If source IsNot Nothing Then - Return New SyntaxTree() {VisualBasicSyntaxTree.ParseText(source, parseOptions)} + Return New SyntaxTree() {VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default), parseOptions)} End If Dim sources = TryCast(Value, String()) If sources IsNot Nothing Then - Return sources.Select(Function(s) VisualBasicSyntaxTree.ParseText(s, parseOptions)).ToArray() + Return sources.Select(Function(s) VisualBasicSyntaxTree.ParseText(SourceText.From(s, encoding:=Nothing, SourceHashAlgorithms.Default), parseOptions)).ToArray() End If Dim tree = TryCast(Value, SyntaxTree) diff --git a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb index 510adb46913d0..4c969e063c190 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb @@ -19,7 +19,7 @@ Imports Xunit Friend Module CompilationUtils Private Function ParseSources(source As IEnumerable(Of String), parseOptions As VisualBasicParseOptions) As IEnumerable(Of SyntaxTree) - Return source.Select(Function(s) VisualBasicSyntaxTree.ParseText(s, parseOptions)) + Return source.Select(Function(s) VisualBasicSyntaxTree.ParseText(SourceText.From(s, encoding:=Nothing, SourceHashAlgorithms.Default), parseOptions)) End Function Public Function CreateCompilation( @@ -622,7 +622,7 @@ Friend Module CompilationUtils ''' </file> ''' Public Function CreateParseTree(programElement As XElement) As SyntaxTree - Return VisualBasicSyntaxTree.ParseText(FilterString(programElement.Value), path:=If(programElement.@name, ""), encoding:=Encoding.UTF8) + Return VisualBasicSyntaxTree.ParseText(SourceText.From(FilterString(programElement.Value), Encoding.UTF8, SourceHashAlgorithms.Default), path:=If(programElement.@name, "")) End Function ''' diff --git a/src/Compilers/Test/Utilities/VisualBasic/VBParser.vb b/src/Compilers/Test/Utilities/VisualBasic/VBParser.vb index 18039220d0bf5..e034b81a69acc 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/VBParser.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/VBParser.vb @@ -5,6 +5,7 @@ Imports System.Text Imports System.Reflection Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.Text Public Class VBParser Private ReadOnly _options As VisualBasicParseOptions @@ -14,7 +15,7 @@ Public Class VBParser End Sub Public Function Parse(code As String) As SyntaxTree - Dim tree = VisualBasicSyntaxTree.ParseText(code, _options, "", Encoding.UTF8) + Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(code, Encoding.UTF8, SourceHashAlgorithms.Default), _options, path:="") Return tree End Function End Class diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb index e1d81931aa71e..4b6b2e5f0df92 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb @@ -6,6 +6,7 @@ Imports System.Collections.Immutable Imports System.Reflection Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts @@ -1078,7 +1079,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic "End Class" & vbCrLf ' It looks like Dev11 ignores project level conditional compilation here, which makes sense since expression cannot contain #If directives. - Dim tree = VisualBasicSyntaxTree.ParseText(codeToParse) + Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(codeToParse)) Dim root As CompilationUnitSyntax = tree.GetCompilationUnitRoot() Dim hasErrors As Boolean = False diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 0cd36c7faab8a..5477df9291e42 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -123,7 +123,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim embeddedFiles = New List(Of CommandLineSourceFile)() Dim embedAllSourceFiles = False Dim codepage As Encoding = Nothing - Dim checksumAlgorithm = SourceHashAlgorithmUtils.DefaultContentHashAlgorithm + Dim checksumAlgorithm = SourceHashAlgorithms.Default Dim defines As IReadOnlyDictionary(Of String, Object) = Nothing Dim metadataReferences = New List(Of CommandLineReference)() Dim analyzers = New List(Of CommandLineAnalyzerReference)() diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 15d8cb3ac559f..0052c4571218e 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -262,7 +262,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' taken to ensure these are compatible with 2.0 runtimes so there is no danger ' with allowing the newer syntax here. Dim options = parseOptions.WithLanguageVersion(LanguageVersion.Default) - tree = VisualBasicSyntaxTree.ParseText(text, options:=options, isMyTemplate:=True) + tree = VisualBasicSyntaxTree.ParseText( + SourceText.From(text, encoding:=Nothing, SourceHashAlgorithms.Default), + isMyTemplate:=True, + options, + path:=Nothing) If tree.GetDiagnostics().Any() Then Throw ExceptionUtilities.Unreachable diff --git a/src/Compilers/VisualBasic/Portable/Symbols/EmbeddedSymbols/EmbeddedSymbolManager.vb b/src/Compilers/VisualBasic/Portable/Symbols/EmbeddedSymbols/EmbeddedSymbolManager.vb index 1b1ad1c4af702..9490bdd465d97 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/EmbeddedSymbols/EmbeddedSymbolManager.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/EmbeddedSymbols/EmbeddedSymbolManager.vb @@ -2,6 +2,7 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Text Imports System.Threading Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols @@ -18,6 +19,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Shared s_vbCoreSyntax As SyntaxTree = Nothing Private Shared s_internalXmlHelperSyntax As SyntaxTree = Nothing + Private Shared Function ParseResourceText(text As String) As SyntaxTree + Return VisualBasicSyntaxTree.ParseText(SourceText.From(text, Encoding.UTF8, SourceHashAlgorithms.Default)) + End Function + Friend Shared Function GetEmbeddedKind(tree As SyntaxTree) As EmbeddedSymbolKind Debug.Assert(tree IsNot Nothing) If tree Is Nothing Then @@ -49,9 +54,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Shared ReadOnly Property EmbeddedSyntax As SyntaxTree Get If s_embeddedSyntax Is Nothing Then - Interlocked.CompareExchange(s_embeddedSyntax, - VisualBasicSyntaxTree.ParseText(EmbeddedResources.Embedded), - Nothing) + Interlocked.CompareExchange(s_embeddedSyntax, ParseResourceText(EmbeddedResources.Embedded), Nothing) If s_embeddedSyntax.GetDiagnostics().Any() Then Throw ExceptionUtilities.Unreachable End If @@ -66,9 +69,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Shared ReadOnly Property VbCoreSyntaxTree As SyntaxTree Get If s_vbCoreSyntax Is Nothing Then - Interlocked.CompareExchange(s_vbCoreSyntax, - VisualBasicSyntaxTree.ParseText(EmbeddedResources.VbCoreSourceText), - Nothing) + Interlocked.CompareExchange(s_vbCoreSyntax, ParseResourceText(EmbeddedResources.VbCoreSourceText), Nothing) If s_vbCoreSyntax.GetDiagnostics().Any() Then Throw ExceptionUtilities.Unreachable End If @@ -80,9 +81,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Shared ReadOnly Property InternalXmlHelperSyntax As SyntaxTree Get If s_internalXmlHelperSyntax Is Nothing Then - Interlocked.CompareExchange(s_internalXmlHelperSyntax, - VisualBasicSyntaxTree.ParseText(EmbeddedResources.InternalXmlHelper), - Nothing) + Interlocked.CompareExchange(s_internalXmlHelperSyntax, ParseResourceText(EmbeddedResources.InternalXmlHelper), Nothing) If s_internalXmlHelperSyntax.GetDiagnostics().Any() Then Throw ExceptionUtilities.Unreachable End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SynthesizedMyGroupCollectionPropertyAccessorSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SynthesizedMyGroupCollectionPropertyAccessorSymbol.vb index c6f0dd3fa1384..05e012e763751 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SynthesizedMyGroupCollectionPropertyAccessorSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SynthesizedMyGroupCollectionPropertyAccessorSymbol.vb @@ -6,6 +6,7 @@ Imports System.Collections.Generic Imports System.Collections.Immutable Imports System.Diagnostics Imports System.Runtime.InteropServices +Imports System.Text Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Emit @@ -70,7 +71,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols "End Class" & vbCrLf ' TODO: It looks like Dev11 respects project level conditional compilation here. - Dim tree = VisualBasicSyntaxTree.ParseText(codeToParse) + Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(codeToParse, Encoding.UTF8, SourceHashAlgorithms.Default)) Dim attributeSyntax = PropertyOrEvent.AttributeSyntax.GetVisualBasicSyntax() Dim diagnosticLocation As Location = attributeSyntax.GetLocation() Dim root As CompilationUnitSyntax = tree.GetCompilationUnitRoot() diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb index f3727094a50cd..8eeee3522ee02 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb @@ -69,7 +69,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If tree Is Nothing Then Debug.Assert(rootCandidate IsNot Nothing) +#Disable Warning RS0030 ' This method is intended to be used from this call site only tree = VisualBasicSyntaxTree.CreateWithoutClone(DirectCast(rootCandidate, VisualBasicSyntaxNode)) +#Enable Warning RS0030 End If Debug.Assert(tree IsNot Nothing) diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb new file mode 100644 index 0000000000000..6f1086a0201b7 --- /dev/null +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb @@ -0,0 +1,32 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.Text + +Namespace Microsoft.CodeAnalysis.VisualBasic + + Partial Public MustInherit Class VisualBasicSyntaxTree + Private NotInheritable Class DebuggerSyntaxTree + Inherits ParsedSyntaxTree + + Friend Sub New(root As VisualBasicSyntaxNode, text As SourceText, options As VisualBasicParseOptions) + MyBase.New(text, + text.Encoding, + text.ChecksumAlgorithm, + path:="", + options, + root, + isMyTemplate:=False, + diagnosticOptions:=Nothing, + cloneRoot:=True) + End Sub + + Friend Overrides ReadOnly Property SupportsLocations As Boolean + Get + Return True + End Get + End Property + End Class + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb index c2aaa28f1b1bc..0428ad1705927 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb @@ -13,6 +13,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend Class DummySyntaxTree Inherits VisualBasicSyntaxTree + Private Const ChecksumAlgorithm As SourceHashAlgorithm = SourceHashAlgorithm.Sha1 + Private ReadOnly _node As CompilationUnitSyntax Public Sub New() @@ -24,11 +26,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText - Return SourceText.From(String.Empty, Encoding.UTF8) + Return SourceText.From(String.Empty, Me.Encoding, ChecksumAlgorithm) End Function Public Overrides Function TryGetText(ByRef text As SourceText) As Boolean - text = SourceText.From(String.Empty, Encoding.UTF8) + text = SourceText.From(String.Empty, Me.Encoding, ChecksumAlgorithm) Return True End Function @@ -86,11 +88,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Property Public Overrides Function WithRootAndOptions(root As SyntaxNode, options As ParseOptions) As SyntaxTree - Return SyntaxFactory.SyntaxTree(root, options:=options, path:=FilePath, encoding:=Nothing) + Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), FilePath, Me.Encoding) End Function Public Overrides Function WithFilePath(path As String) As SyntaxTree - Return SyntaxFactory.SyntaxTree(_node, options:=Me.Options, path:=path, encoding:=Nothing) + Return VisualBasicSyntaxTree.Create(_node, Options, path, Me.Encoding) End Function Public Overrides Function WithDiagnosticOptions(options As ImmutableDictionary(Of String, ReportDiagnostic)) As SyntaxTree diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ParsedSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ParsedSyntaxTree.vb index 5a867aec51409..932e149d66a46 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ParsedSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ParsedSyntaxTree.vb @@ -43,10 +43,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic diagnosticOptions As ImmutableDictionary(Of String, ReportDiagnostic), Optional cloneRoot As Boolean = True) - Debug.Assert(syntaxRoot IsNot Nothing) Debug.Assert(options IsNot Nothing) Debug.Assert(textOpt Is Nothing OrElse textOpt.Encoding Is encodingOpt AndAlso textOpt.ChecksumAlgorithm = checksumAlgorithm) + Debug.Assert(SourceHashAlgorithms.IsSupportedAlgorithm(checksumAlgorithm)) _lazyText = textOpt _encodingOpt = If(encodingOpt, textOpt?.Encoding) diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb index 0370605d0b81e..742dde6152601 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb @@ -6,7 +6,6 @@ Imports System.Collections.Immutable Imports System.ComponentModel Imports System.Text Imports System.Threading -Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -178,6 +177,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic diagnosticOptions) End Function + ''' + ''' Creates a new syntax tree from a syntax node with text that should correspond to the syntax node. + ''' + ''' This is used by the ExpressionEvaluator. + Friend Shared Function CreateForDebugger(root As VisualBasicSyntaxNode, text As SourceText, options As VisualBasicParseOptions) As SyntaxTree + Debug.Assert(root IsNot Nothing) + Return New DebuggerSyntaxTree(root, text, options) + End Function + ''' ''' ''' Internal helper for class to create a new syntax tree rooted at the given root node. @@ -589,7 +597,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic path As String, encoding As Encoding, cancellationToken As CancellationToken) As SyntaxTree +#Disable Warning RS0030 ' Do not used banned APIs Return ParseText(text, options, path, encoding, diagnosticOptions:=Nothing, cancellationToken) +#Enable Warning RS0030 End Function ' 2.8 BACK COMPAT OVERLOAD -- DO NOT MODIFY @@ -607,7 +617,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic options As VisualBasicParseOptions, path As String, encoding As Encoding) As SyntaxTree +#Disable Warning RS0030 ' Do not used banned APIs Return Create(root, options, path, encoding, diagnosticOptions:=Nothing) +#Enable Warning RS0030 End Function End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb index 8039de658d6eb..1561d89c82fde 100644 --- a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb @@ -66,6 +66,38 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.PDB End Sub + + Public Sub EmitDebugInfoForSynthesizedSyntaxTree() + Dim tree1 = SyntaxFactory.ParseCompilationUnit(" +#ExternalSource(""test.vb"", 1) +Class C + Sub M + End Sub +End Class +#End ExternalSource +").SyntaxTree + Dim tree2 = SyntaxFactory.ParseCompilationUnit(" +Class D + Sub M + End Sub +End Class +").SyntaxTree + + Dim comp = VisualBasicCompilation.Create("test", {tree1, tree2}, TargetFrameworkUtil.StandardReferences, TestOptions.DebugDll) + + Dim result = comp.Emit(New MemoryStream(), pdbStream:=New MemoryStream()) + result.Diagnostics.Verify() + + comp.VerifyPdb(" + + + + + + +", format:=DebugInformationFormat.PortablePdb, options:=PdbValidationOptions.ExcludeMethods) + End Sub + Public Sub CustomDebugEntryPoint_DLL() Dim source = " diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/MyTemplateTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/MyTemplateTests.vb index b1909f952c96f..0b94b4c16e7b6 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/MyTemplateTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/MyTemplateTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Roslyn.Test.Utilities @@ -48,9 +49,9 @@ End Class Assert.NotNull(MyTemplate) - Dim text = MyTemplate.GetText.ToString - Assert.Contains("Private ReadOnly m_Context As New Global.Microsoft.VisualBasic.MyServices.Internal.ContextValue(Of T)", text, StringComparison.Ordinal) - + Dim sourceText = MyTemplate.GetText() + Assert.Contains("Private ReadOnly m_Context As New Global.Microsoft.VisualBasic.MyServices.Internal.ContextValue(Of T)", sourceText.ToString(), StringComparison.Ordinal) + Assert.Equal(SourceHashAlgorithms.Default, sourceText.ChecksumAlgorithm) End Sub diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/GroupClassTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/GroupClassTests.vb index a2accc8ce72e4..97791e8682251 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/GroupClassTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/GroupClassTests.vb @@ -5,12 +5,20 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.Test.Utilities Imports Roslyn.Test.Utilities +Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Class GroupClassTests Inherits BasicTestBase + Private Shared Function ParseTemplateTree(text As String, Optional path As String = Nothing) As SyntaxTree + Return VisualBasicSyntaxTree.ParseText( + SourceText.From(text, encoding:=Nothing, checksumAlgorithm:=SourceHashAlgorithms.Default), + isMyTemplate:=True, + path:=path) + End Function + Public Sub SimpleTest1() Dim compilationDef = @@ -2229,7 +2237,7 @@ End Namespace #End If ]]>.Value - Friend Shared ReadOnly WindowsFormsMyTemplateTree As SyntaxTree = VisualBasicSyntaxTree.ParseText(WindowsFormsMyTemplateSource, isMyTemplate:=True, path:="17d14f5c-a337-4978-8281-53493378c107.vb") ' The name used by native compiler + Friend Shared ReadOnly WindowsFormsMyTemplateTree As SyntaxTree = ParseTemplateTree(WindowsFormsMyTemplateSource, path:="17d14f5c-a337-4978-8281-53493378c107.vb") ' The name used by native compiler Public Sub DefaultInstanceAlias1() @@ -2859,7 +2867,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertTheseDiagnostics(compilation, @@ -2920,7 +2928,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertTheseDiagnostics(compilation, @@ -3001,7 +3009,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertTheseDiagnostics(compilation, @@ -3097,7 +3105,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertTheseDiagnostics(compilation, @@ -3159,7 +3167,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertTheseDiagnostics(compilation, @@ -3225,7 +3233,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertTheseDiagnostics(compilation, @@ -3310,7 +3318,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertNoDiagnostics(compilation) @@ -3388,7 +3396,7 @@ End Class Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(compilationDef, TestOptions.ReleaseDll) - compilation.MyTemplate = VisualBasicSyntaxTree.ParseText( + compilation.MyTemplate = ParseTemplateTree( .Value, isMyTemplate:=True) +]]>.Value) AssertTheseDiagnostics(compilation, diff --git a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb index 2bd43c040f45c..5f48ec6ab77b4 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb @@ -10,16 +10,22 @@ Imports Roslyn.Test.Utilities.TestHelpers Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Class VisualBasicSyntaxTreeTests - ' Diagnostic options on syntax trees are now obsolete -#Disable warning BC40000 - Public Sub CreateTreeWithDiagnosticOptions() + Public Sub Create() + Dim root = SyntaxFactory.ParseCompilationUnit("") + + Dim tree = VisualBasicSyntaxTree.Create(root) + Assert.Equal(SourceHashAlgorithm.Sha1, tree.GetText().ChecksumAlgorithm) + End Sub + + + Public Sub Create_WithDiagnosticOptions() Dim options = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) - Dim tree = VisualBasicSyntaxTree.Create(SyntaxFactory.ParseCompilationUnit(""), diagnosticOptions:=options) + Dim tree = VisualBasicSyntaxTree.Create(SyntaxFactory.ParseCompilationUnit(""), options:=Nothing, path:=Nothing, encoding:=Nothing, diagnosticOptions:=options) Assert.Same(options, tree.DiagnosticOptions) End Sub - + Public Sub ParseTreeWithChangesPreservesDiagnosticOptions() Dim options = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) Dim tree = VisualBasicSyntaxTree.ParseText( @@ -30,7 +36,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(options, newTree.DiagnosticOptions) End Sub - + Public Sub ParseTreeNullDiagnosticOptions() Dim tree = VisualBasicSyntaxTree.ParseText( SourceText.From(""), @@ -41,7 +47,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.NotSame(ImmutableDictionary(Of String, ReportDiagnostic).Empty, tree.DiagnosticOptions) End Sub - + Public Sub ParseTreeEmptyDiagnosticOptions() Dim tree = VisualBasicSyntaxTree.ParseText( SourceText.From(""), @@ -51,7 +57,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(ImmutableDictionary(Of String, ReportDiagnostic).Empty, tree.DiagnosticOptions) End Sub - + Public Sub ParseTreeCustomDiagnosticOptions() Dim options = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) Dim tree = VisualBasicSyntaxTree.ParseText( @@ -60,14 +66,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(options, tree.DiagnosticOptions) End Sub - + Public Sub DefaultTreeDiagnosticOptions() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Assert.NotNull(tree.DiagnosticOptions) Assert.True(tree.DiagnosticOptions.IsEmpty) End Sub - + Public Sub WithDiagnosticOptionsNull() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Dim newTree = tree.WithDiagnosticOptions(Nothing) @@ -76,7 +82,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(tree, newTree) End Sub - + Public Sub WithDiagnosticOptionsEmpty() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Dim newTree = tree.WithDiagnosticOptions(ImmutableDictionary(Of String, ReportDiagnostic).Empty) @@ -86,7 +92,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.NotSame(tree.DiagnosticOptions, newTree.DiagnosticOptions) End Sub - + Public Sub PerTreeDiagnosticOptionsNewDict() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Dim map = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) @@ -95,7 +101,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(map, newTree.DiagnosticOptions) Assert.NotEqual(tree, newTree) End Sub -#Enable warning BC40000 Public Sub WithRootAndOptions_ParsedTree() @@ -114,7 +119,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Sub WithRootAndOptions_ParsedTreeWithText() - Dim oldText = SourceText.From("Class B : End Class", Encoding.Unicode, SourceHashAlgorithm.Sha256) + Dim oldText = SourceText.From("Class B : End Class", Encoding.Unicode, SourceHashAlgorithms.Default) Dim oldTree = SyntaxFactory.ParseSyntaxTree(oldText) Dim newRoot = SyntaxFactory.ParseCompilationUnit("Class C : End Class") @@ -126,7 +131,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(newOptions, newTree.Options) Assert.Same(Encoding.Unicode, newText.Encoding) - Assert.Equal(SourceHashAlgorithm.Sha256, newText.ChecksumAlgorithm) + Assert.Equal(SourceHashAlgorithms.Default, newText.ChecksumAlgorithm) End Sub @@ -154,7 +159,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Sub WithFilePath_ParsedTreeWithText() - Dim oldText = SourceText.From("Class B : End Class", Encoding.Unicode, SourceHashAlgorithm.Sha256) + Dim oldText = SourceText.From("Class B : End Class", Encoding.Unicode, SourceHashAlgorithms.Default) Dim oldTree = SyntaxFactory.ParseSyntaxTree(oldText, path:="old.vb") Dim newTree = oldTree.WithFilePath("new.vb") Dim newText = newTree.GetText() @@ -163,7 +168,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Equal(oldTree.ToString(), newTree.ToString()) Assert.Same(Encoding.Unicode, newText.Encoding) - Assert.Equal(SourceHashAlgorithm.Sha256, newText.ChecksumAlgorithm) + Assert.Equal(SourceHashAlgorithms.Default, newText.ChecksumAlgorithm) End Sub diff --git a/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs b/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs index cfdb436eefda5..cce225223bf3e 100644 --- a/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs +++ b/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs @@ -110,7 +110,7 @@ public static async Task FormatDocumentAsync(Document document, Syntax text += logger.ToString(); text += "#endif" + Environment.NewLine; - return document.WithText(SourceText.From(text)); + return document.WithText(SourceText.From(text, encoding: null, checksumAlgorithm: SourceHashAlgorithms.Default)); } private static async Task AddAssemblyInfoRegionAsync(Document document, ISymbol symbol, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs b/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs index c57f0a47e14fe..94edfb224c348 100644 --- a/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs +++ b/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting; using Microsoft.CodeAnalysis.Interactive; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Editor.CSharp.Interactive { @@ -44,7 +45,7 @@ public override CompilationOptions GetSubmissionCompilationOptions(string name, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default)); public override bool IsCompleteSubmission(string text) - => SyntaxFactory.IsCompleteSubmission(SyntaxFactory.ParseSyntaxTree(text, options: s_parseOptions)); + => SyntaxFactory.IsCompleteSubmission(SyntaxFactory.ParseSyntaxTree(SourceText.From(text, encoding: null, SourceHashAlgorithms.Default), options: s_parseOptions)); public override string InteractiveResponseFileName => "CSharpInteractive.rsp"; diff --git a/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs b/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs index bbba2c42e0e39..5872fee4ea624 100644 --- a/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs +++ b/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs @@ -28,7 +28,7 @@ public CSharpSendToInteractiveSubmissionProvider() protected override bool CanParseSubmission(string code) { var options = CSharpInteractiveEvaluatorLanguageInfoProvider.Instance.ParseOptions; - var tree = SyntaxFactory.ParseSyntaxTree(code, options); + var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(code, encoding: null, SourceHashAlgorithms.Default), options); return tree.HasCompilationUnitRoot && !tree.GetDiagnostics().Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error); } diff --git a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs index fbb1a3e15056d..cd5f5e8c6ef8c 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs @@ -53,16 +53,16 @@ void F () {} } "; + var sourceText = SourceText.From(source, encoding: null, SourceHashAlgorithms.Default); var documentInfo = DocumentInfo.Create( DocumentId.CreateNewId(project.Id), name: "file.razor.g.cs", folders: Array.Empty(), sourceCodeKind: SourceCodeKind.Regular, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From(source), VersionStamp.Create(), "file.razor.g.cs")), - filePath: "file.razor.g.cs", - isGenerated: false, - designTimeOnly: true, - documentServiceProvider: new TestRazorDocumentServiceProvider()); + loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), "file.razor.g.cs")), + filePath: "file.razor.g.cs") + .WithDesignTimeOnly(true) + .WithDocumentServiceProvider(new TestRazorDocumentServiceProvider()); var document = workspace.AddDocument(documentInfo); diff --git a/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs index 3bce152b83464..02b69bd556528 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs @@ -43,7 +43,7 @@ await RunTestAsync(async path => using var hash = SHA256.Create(); var fileHash = hash.ComputeHash(File.ReadAllBytes(sourceFilePath)); - var sourceDocument = new SourceDocument("goo.cs", Text.SourceHashAlgorithm.Sha256, fileHash.ToImmutableArray(), null, "https://sourcelink"); + var sourceDocument = new SourceDocument("goo.cs", Text.SourceHashAlgorithms.Default, fileHash.ToImmutableArray(), null, "https://sourcelink"); var result = await service.LoadSourceDocumentAsync(path, sourceDocument, Encoding.UTF8, new TelemetryMessage(CancellationToken.None), useExtendedTimeout: false, CancellationToken.None); Assert.NotNull(result); diff --git a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs index fe4b91a12eab6..914e0f62ccd6f 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; @@ -233,7 +234,7 @@ private void AddSubmissionProjectNoLock(ITextBuffer submissionBuffer, string lan solution = initProject.Solution.AddDocument( DocumentId.CreateNewId(initializationScriptProjectId, debugName: initializationScriptPath), Path.GetFileName(initializationScriptPath), - new WorkspaceFileTextLoader(solution.Services, initializationScriptPath, defaultEncoding: null)); + new WorkspaceFileTextLoader(solution.Services, initializationScriptPath, defaultEncoding: null, initProject.State.ChecksumAlgorithm)); } var newSubmissionProject = CreateSubmissionProjectNoLock(solution, _currentSubmissionProjectId, _lastSuccessfulSubmissionProjectId, languageName, imports, references); @@ -289,18 +290,21 @@ private Project CreateSubmissionProjectNoLock(Solution solution, ProjectId newSu solution = solution.AddProject( ProjectInfo.Create( - newSubmissionProjectId, - VersionStamp.Create(), - name: name, - assemblyName: name, - language: languageName, + new ProjectInfo.ProjectAttributes( + id: newSubmissionProjectId, + version: VersionStamp.Create(), + name: name, + assemblyName: name, + language: languageName, + compilationOutputFilePaths: default, + checksumAlgorithm: SourceHashAlgorithms.Default, + isSubmission: true), compilationOptions: compilationOptions, parseOptions: _languageInfo.ParseOptions, documents: null, projectReferences: null, metadataReferences: references, - hostObjectType: typeof(InteractiveScriptGlobals), - isSubmission: true)); + hostObjectType: typeof(InteractiveScriptGlobals))); if (previousSubmissionProjectId != null) { diff --git a/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs b/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs index f3f931c48bbb7..23a4fcaec4e54 100644 --- a/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs +++ b/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs @@ -37,7 +37,7 @@ public EditorTextFactoryService( private static readonly Encoding s_throwingUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken) + public SourceText CreateText(Stream stream, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { // this API is for a case where user wants us to figure out encoding from the given stream. // if defaultEncoding is given, we will use it if we couldn't figure out encoding used in the stream ourselves. @@ -50,7 +50,7 @@ public SourceText CreateText(Stream stream, Encoding? defaultEncoding, Cancellat // Try UTF-8 try { - return CreateTextInternal(stream, s_throwingUtf8Encoding, cancellationToken); + return CreateTextInternal(stream, s_throwingUtf8Encoding, checksumAlgorithm, cancellationToken); } catch (DecoderFallbackException) { @@ -61,7 +61,7 @@ public SourceText CreateText(Stream stream, Encoding? defaultEncoding, Cancellat try { - return CreateTextInternal(stream, defaultEncoding, cancellationToken); + return CreateTextInternal(stream, defaultEncoding, checksumAlgorithm, cancellationToken); } catch (DecoderFallbackException) { @@ -70,19 +70,19 @@ public SourceText CreateText(Stream stream, Encoding? defaultEncoding, Cancellat } } - public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken) + public SourceText CreateText(TextReader reader, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { // this API is for a case where user just wants to create a source text with explicit encoding. var buffer = CreateTextBuffer(reader); // use the given encoding as it is. - return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, encoding); + return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, encoding, checksumAlgorithm); } private ITextBuffer CreateTextBuffer(TextReader reader) => _textBufferFactory.CreateTextBuffer(reader, _unknownContentType); - private SourceText CreateTextInternal(Stream stream, Encoding encoding, CancellationToken cancellationToken) + private SourceText CreateTextInternal(Stream stream, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); stream.Seek(0, SeekOrigin.Begin); @@ -90,7 +90,7 @@ private SourceText CreateTextInternal(Stream stream, Encoding encoding, Cancella using var reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true); var buffer = CreateTextBuffer(reader); - return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, reader.CurrentEncoding ?? Encoding.UTF8); + return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, reader.CurrentEncoding ?? Encoding.UTF8, checksumAlgorithm); } } } diff --git a/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs b/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs index aabae5d7cd35a..f5cace812b627 100644 --- a/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs @@ -54,13 +54,9 @@ public async Task TryGetCompileTimeDocumentAsync(string kind) AddDocument(DocumentInfo.Create( designTimeDocumentId, name: "a", - folders: Array.Empty(), - sourceCodeKind: SourceCodeKind.Regular, loader: null, filePath: designTimeFilePath, - isGenerated: true, - designTimeOnly: true, - documentServiceProvider: null)); + isGenerated: true).WithDesignTimeOnly(true)); var designTimeDocument = designTimeSolution.GetRequiredDocument(designTimeDocumentId); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs index cf5dede5510f0..76fc9c17425f0 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; using Microsoft.DiaSymReader; using Microsoft.DiaSymReader.PortablePdb; diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index bc2d0ad73b8f7..1ae3bd7416cd5 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -283,7 +283,7 @@ private Guid EmitLibrary( var trees = sources.Select(source => { - var sourceText = SourceText.From(new MemoryStream(encoding.GetBytes(source.content)), encoding, checksumAlgorithm: SourceHashAlgorithm.Sha256); + var sourceText = SourceText.From(new MemoryStream(encoding.GetBytes(source.content)), encoding, checksumAlgorithm: SourceHashAlgorithms.Default); return SyntaxFactory.ParseSyntaxTree(sourceText, parseOptions, source.filePath); }); @@ -335,7 +335,7 @@ private Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbForm private static SourceText CreateSourceTextFromFile(string path) { using var stream = File.OpenRead(path); - return SourceText.From(stream, Encoding.UTF8, SourceHashAlgorithm.Sha256); + return SourceText.From(stream, Encoding.UTF8, SourceHashAlgorithms.Default); } private static TextSpan GetSpan(string str, string substr) @@ -360,16 +360,17 @@ private static void VerifyReadersDisposed(IEnumerable readers) } private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, string name = "design-time-only.cs", string path = "design-time-only.cs") - => DocumentInfo.Create( + { + var sourceText = SourceText.From("class DTO {}", Encoding.UTF8, SourceHashAlgorithms.Default); + return DocumentInfo.Create( DocumentId.CreateNewId(projectId, name), name: name, folders: Array.Empty(), sourceCodeKind: SourceCodeKind.Regular, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From("class DTO {}"), VersionStamp.Create(), path)), + loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), path)), filePath: path, - isGenerated: false, - designTimeOnly: true, - documentServiceProvider: null); + isGenerated: false).WithDesignTimeOnly(true); + } internal sealed class FailingTextLoader : TextLoader { @@ -476,8 +477,8 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume var sourceFileC = dir.CreateFile("C.cs").WriteAllBytes(sourceBytesC1); var sourceFileD = dir.CreateFile("dummy").WriteAllText(sourceD1); var sourceFileE = dir.CreateFile("E.cs").WriteAllBytes(sourceBytesE1); - var sourceTreeA1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesA1, sourceBytesA1.Length, encodingA, SourceHashAlgorithm.Sha256), TestOptions.Regular, sourceFileA.Path); - var sourceTreeB1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesB1, sourceBytesB1.Length, encodingB, SourceHashAlgorithm.Sha256), TestOptions.Regular, sourceFileB.Path); + var sourceTreeA1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesA1, sourceBytesA1.Length, encodingA, SourceHashAlgorithms.Default), TestOptions.Regular, sourceFileA.Path); + var sourceTreeB1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesB1, sourceBytesB1.Length, encodingB, SourceHashAlgorithms.Default), TestOptions.Regular, sourceFileB.Path); var sourceTreeC1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesC1, sourceBytesC1.Length, encodingC, SourceHashAlgorithm.Sha1), TestOptions.Regular, sourceFileC.Path); // E is not included in the compilation: @@ -497,28 +498,28 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA, SourceHashAlgorithm.Sha1), filePath: sourceFileA.Path)); var documentIdB = DocumentId.CreateNewId(projectP.Id, debugName: "B"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdB, name: "B", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB, SourceHashAlgorithm.Sha1), filePath: sourceFileB.Path)); var documentIdC = DocumentId.CreateNewId(projectP.Id, debugName: "C"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdC, name: "C", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC, SourceHashAlgorithm.Sha1), filePath: sourceFileC.Path)); var documentIdE = DocumentId.CreateNewId(projectP.Id, debugName: "E"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdE, name: "E", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE, SourceHashAlgorithm.Sha1), filePath: sourceFileE.Path)); // check that are testing documents whose hash algorithm does not match the PDB (but the hash itself does): @@ -558,7 +559,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume // change content of B on disk again: sourceFileB.WriteAllText(sourceB3, encodingB); - solution = solution.WithDocumentTextLoader(documentIdB, new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), PreservationMode.PreserveValue); + solution = solution.WithDocumentTextLoader(documentIdB, new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB, SourceHashAlgorithms.Default), PreservationMode.PreserveValue); EnterBreakState(debuggingSession); @@ -713,16 +714,13 @@ public async Task DesignTimeOnlyDocument_Dynamic() (solution, var document) = AddDefaultTestProject(solution, "class C {}"); + var sourceText = SourceText.From("class D {}", Encoding.UTF8, SourceHashAlgorithms.Default); var documentInfo = DocumentInfo.Create( DocumentId.CreateNewId(document.Project.Id), name: "design-time-only.cs", - folders: Array.Empty(), - sourceCodeKind: SourceCodeKind.Regular, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From("class D {}"), VersionStamp.Create(), "design-time-only.cs")), + loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), "design-time-only.cs")), filePath: "design-time-only.cs", - isGenerated: false, - designTimeOnly: true, - documentServiceProvider: null); + isGenerated: false).WithDesignTimeOnly(true); solution = solution.AddDocument(documentInfo); @@ -1900,8 +1898,8 @@ public async Task HasChanges_Documents(DocumentKind documentKind) var documentId = DocumentId.CreateNewId(projectId); solution = documentKind switch { - DocumentKind.Source => solution.AddDocument(documentId, "X", SourceText.From("xxx", Encoding.UTF8, SourceHashAlgorithm.Sha256), filePath: pathX), - DocumentKind.Additional => solution.AddAdditionalDocument(documentId, "X", SourceText.From("xxx", Encoding.UTF8, SourceHashAlgorithm.Sha256), filePath: pathX), + DocumentKind.Source => solution.AddDocument(documentId, "X", SourceText.From("xxx", Encoding.UTF8, SourceHashAlgorithms.Default), filePath: pathX), + DocumentKind.Additional => solution.AddAdditionalDocument(documentId, "X", SourceText.From("xxx", Encoding.UTF8, SourceHashAlgorithms.Default), filePath: pathX), DocumentKind.AnalyzerConfig => solution.AddAnalyzerConfigDocument(documentId, "X", GetAnalyzerConfigText(new[] { ("x", "1") }), filePath: pathX), _ => throw ExceptionUtilities.Unreachable(), }; @@ -1931,8 +1929,8 @@ public async Task HasChanges_Documents(DocumentKind documentKind) solution = documentKind switch { - DocumentKind.Source => solution.WithDocumentText(documentId, SourceText.From("xxx", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithm.Sha256)), - DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, SourceText.From("xxx", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithm.Sha256)), + DocumentKind.Source => solution.WithDocumentText(documentId, SourceText.From("xxx", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), + DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, SourceText.From("xxx", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText(new[] { ("x", "1") })), _ => throw ExceptionUtilities.Unreachable(), }; @@ -1958,8 +1956,8 @@ public async Task HasChanges_Documents(DocumentKind documentKind) oldSolution = solution; solution = documentKind switch { - DocumentKind.Source => solution.WithDocumentText(documentId, SourceText.From("xxx-changed", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithm.Sha256)), - DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, SourceText.From("xxx-changed", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithm.Sha256)), + DocumentKind.Source => solution.WithDocumentText(documentId, SourceText.From("xxx-changed", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), + DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, SourceText.From("xxx-changed", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText(new[] { ("x", "2") })), _ => throw ExceptionUtilities.Unreachable(), }; @@ -2045,7 +2043,7 @@ public async Task Project_Add() var documentB2 = solution. AddProject("B", "B", LanguageNames.CSharp). - AddDocument("b.cs", SourceText.From(sourceB2, Encoding.UTF8, SourceHashAlgorithm.Sha256), filePath: sourceFileB.Path); + AddDocument("b.cs", SourceText.From(sourceB2, Encoding.UTF8, SourceHashAlgorithms.Default), filePath: sourceFileB.Path); solution = documentB2.Project.Solution; @@ -2078,7 +2076,7 @@ public async Task Project_Add() Assert.Empty(diagnostics); // update document with a valid change: - solution = solution.WithDocumentText(documentB2.Id, SourceText.From(sourceB3, Encoding.UTF8, SourceHashAlgorithm.Sha256)); + solution = solution.WithDocumentText(documentB2.Id, SourceText.From(sourceB3, Encoding.UTF8, SourceHashAlgorithms.Default)); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -3629,17 +3627,13 @@ public async Task ActiveStatements_ForeignDocument(bool withPath, bool designTim var project = solution.AddProject("dummy_proj", "dummy_proj", designTimeOnly ? LanguageNames.CSharp : NoCompilationConstants.LanguageName); var filePath = withPath ? Path.Combine(TempRoot.Root, "test.cs") : null; + var sourceText = SourceText.From("dummy1", Encoding.UTF8, SourceHashAlgorithms.Default); var documentInfo = DocumentInfo.Create( DocumentId.CreateNewId(project.Id, "test"), name: "test", - folders: Array.Empty(), - sourceCodeKind: SourceCodeKind.Regular, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From("dummy1"), VersionStamp.Create(), filePath)), - filePath: filePath, - isGenerated: false, - designTimeOnly: designTimeOnly, - documentServiceProvider: null); + loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), filePath)), + filePath: filePath).WithDesignTimeOnly(designTimeOnly); var document = project.Solution.AddDocument(documentInfo).GetDocument(documentInfo.Id); @@ -4399,7 +4393,7 @@ public async Task MultiSession() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8, SourceHashAlgorithms.Default), filePath: sourceFileA.Path)); var tasks = Enumerable.Range(0, 10).Select(async i => @@ -4485,7 +4479,7 @@ public async Task WatchHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8, SourceHashAlgorithms.Default), filePath: sourceFileA.Path)); var hotReload = new WatchHotReloadService(workspace.Services, ImmutableArray.Create("Baseline", "AddDefinitionToExistingType", "NewTypeDefinition")); @@ -4552,7 +4546,7 @@ public async Task UnitTestingHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8, SourceHashAlgorithms.Default), filePath: sourceFileA.Path)); var hotReload = new UnitTestingHotReloadService(workspace.Services); diff --git a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs index 6d8daec08ab71..e4cfb8c9cf9da 100644 --- a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs @@ -120,7 +120,7 @@ public async Task TestCreateFromTemporaryStorageWithEncoding() private static void TestCreateTextInferredEncoding(ITextFactoryService textFactoryService, byte[] bytes, Encoding? defaultEncoding, Encoding expectedEncoding) { using var stream = new MemoryStream(bytes); - var text = textFactoryService.CreateText(stream, defaultEncoding, CancellationToken.None); + var text = textFactoryService.CreateText(stream, defaultEncoding, SourceHashAlgorithms.Default, CancellationToken.None); Assert.Equal(expectedEncoding, text.Encoding); } } diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs index 6994052203229..f307edda5d1a2 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs @@ -25,7 +25,7 @@ public static ImmutableArray GetActiveStatement ActiveStatementFlags[]? flags = null) { return ActiveStatementsDescription.GetActiveStatementDebugInfos( - (source, path) => SyntaxFactory.ParseSyntaxTree(source, path: path), + (source, path) => SyntaxFactory.ParseSyntaxTree(SourceText.From(source, encoding: null, SourceHashAlgorithms.Default), path: path), markedSources, filePaths, extension: ".cs", diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index 6c962098ef5ee..9b93a4dcdae5f 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -382,7 +382,16 @@ private static void VerifySyntaxMap( private void CreateProjects(EditScript[] editScripts, AdhocWorkspace workspace, TargetFramework targetFramework, out Project oldProject, out Project newProject) { - var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), name: "project", assemblyName: "project", LanguageName, filePath: Path.Combine(TempRoot.Root, "project" + ProjectFileExtension)); + var projectInfo = ProjectInfo.Create( + new ProjectInfo.ProjectAttributes( + id: ProjectId.CreateNewId(), + version: VersionStamp.Create(), + name: "project", + assemblyName: "project", + language: LanguageName, + compilationOutputFilePaths: default, + filePath: Path.Combine(TempRoot.Root, "project" + ProjectFileExtension), + checksumAlgorithm: SourceHashAlgorithms.Default)); oldProject = workspace.AddProject(projectInfo).WithMetadataReferences(TargetFrameworkUtil.GetReferences(targetFramework)); foreach (var editScript in editScripts) diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 9fa64d171a674..a39f4441e2613 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -333,7 +333,7 @@ private async Task CreateTestLspServerAsync(TestWorkspace workspa solution = solution.WithDocumentFilePath(document.Id, GetDocumentFilePathFromName(document.Name)); var documentText = await solution.GetRequiredDocument(document.Id).GetTextAsync(CancellationToken.None); - solution = solution.WithDocumentText(document.Id, SourceText.From(documentText.ToString(), System.Text.Encoding.UTF8)); + solution = solution.WithDocumentText(document.Id, SourceText.From(documentText.ToString(), System.Text.Encoding.UTF8, SourceHashAlgorithms.Default)); } foreach (var project in workspace.Projects) @@ -397,8 +397,14 @@ protected static void AddMappedDocument(Workspace workspace, string markup) var generatedDocumentId = DocumentId.CreateNewId(workspace.CurrentSolution.ProjectIds.First()); var version = VersionStamp.Create(); var loader = TextLoader.From(TextAndVersion.Create(SourceText.From(markup), version, TestSpanMapper.GeneratedFileName)); - var generatedDocumentInfo = DocumentInfo.Create(generatedDocumentId, TestSpanMapper.GeneratedFileName, SpecializedCollections.EmptyReadOnlyList(), - SourceCodeKind.Regular, loader, $"C:\\{TestSpanMapper.GeneratedFileName}", isGenerated: true, designTimeOnly: false, new TestSpanMapperProvider()); + var generatedDocumentInfo = DocumentInfo.Create( + generatedDocumentId, + TestSpanMapper.GeneratedFileName, + loader: loader, + filePath: $"C:\\{TestSpanMapper.GeneratedFileName}", + isGenerated: true) + .WithDocumentServiceProvider(new TestSpanMapperProvider()); + var newSolution = workspace.CurrentSolution.AddDocument(generatedDocumentInfo); workspace.TryApplyChanges(newSolution); } diff --git a/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs b/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs index 18e982dec9f9c..7a8e3d2875a09 100644 --- a/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs +++ b/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs @@ -219,19 +219,17 @@ protected async Task TestRenameMappedFile(string startText, string documentName, solution = solution.AddProject(projectInfo); - var startSourceText = SourceText.From(startText); + var startSourceText = SourceText.From(startText, encoding: null, SourceHashAlgorithms.Default); var documentId = DocumentId.CreateNewId(projectId); var documentInfo = DocumentInfo.Create( documentId, documentName, - GetDocumentFolders(s_defaultDocumentPath), - SourceCodeKind.Regular, - TextLoader.From(TextAndVersion.Create(startSourceText, VersionStamp.Create(), documentName)), - s_defaultDocumentPath, - isGenerated: true, - designTimeOnly: false, - new TestDocumentServiceProvider()); + folders: GetDocumentFolders(s_defaultDocumentPath), + loader: TextLoader.From(TextAndVersion.Create(startSourceText, VersionStamp.Create(), documentName)), + filePath: s_defaultDocumentPath, + isGenerated: true) + .WithDocumentServiceProvider(new TestDocumentServiceProvider()); solution = solution.AddDocument(documentInfo); diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index af07e370bad4b..22fbd70ae14b0 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -49,6 +49,7 @@ public class TestHostDocument private readonly IReadOnlyList? _folders; private readonly IDocumentServiceProvider? _documentServiceProvider; private readonly ImmutableArray _roles; + private readonly TestDocumentLoader _loader; public DocumentId Id { @@ -83,8 +84,8 @@ public TestHostProject Project public string Name { get; } public SourceCodeKind SourceCodeKind { get; } public string? FilePath { get; } + public SourceHashAlgorithm ChecksumAlgorithm { get; } = SourceHashAlgorithms.Default; - public TextLoader Loader { get; } public int? CursorPosition { get; } public IList SelectedSpans { get; } = new List(); public IDictionary> AnnotatedSpans { get; } = new Dictionary>(); @@ -147,7 +148,7 @@ internal TestHostDocument( this.AnnotatedSpans.Add(namedSpanList); } - Loader = new TestDocumentLoader(this, _initialText); + _loader = new TestDocumentLoader(this, _initialText); if (textBuffer != null) { @@ -171,7 +172,7 @@ internal TestHostDocument( _initialText = text; Name = displayName; SourceCodeKind = sourceCodeKind; - Loader = new TestDocumentLoader(this, text); + _loader = new TestDocumentLoader(this, text); FilePath = filePath; _folders = folders; _roles = s_defaultRoles; @@ -198,7 +199,7 @@ internal void SetProject(TestHostProject project) _languageServiceProvider ??= project.LanguageServiceProvider; } - private class TestDocumentLoader : TextLoader + private sealed class TestDocumentLoader : TextLoader { private readonly TestHostDocument _hostDocument; private readonly string _text; @@ -209,10 +210,18 @@ internal TestDocumentLoader(TestHostDocument hostDocument, string text) _text = text; } + internal override SourceHashAlgorithm ChecksumAlgorithm + => _hostDocument.ChecksumAlgorithm; + + internal override string? FilePath + => _hostDocument.FilePath; + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create(), _hostDocument.FilePath)); + => Task.FromResult(TextAndVersion.Create(SourceText.From(_text, encoding: null, _hostDocument.ChecksumAlgorithm), VersionStamp.Create(), _hostDocument.FilePath)); } + public TextLoader Loader => _loader; + public IWpfTextView GetTextView() { if (_textView == null) @@ -352,7 +361,7 @@ internal void CloseTextView() public DocumentInfo ToDocumentInfo() { Contract.ThrowIfTrue(IsSourceGenerated, "We shouldn't be producing a DocumentInfo for a source generated document."); - return DocumentInfo.Create(this.Id, this.Name, this.Folders, this.SourceCodeKind, loader: this.Loader, filePath: this.FilePath, isGenerated: false, designTimeOnly: false, _documentServiceProvider); + return DocumentInfo.Create(Id, Name, Folders, SourceCodeKind, Loader, FilePath).WithDocumentServiceProvider(_documentServiceProvider); } } } diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostProject.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostProject.cs index 3876043e91aa8..0c01a96fa5af0 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostProject.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostProject.cs @@ -344,24 +344,27 @@ internal HostLanguageServices LanguageServiceProvider public ProjectInfo ToProjectInfo() { return ProjectInfo.Create( - Id, - Version, - Name, - AssemblyName, - Language, - FilePath, - OutputFilePath, + new ProjectInfo.ProjectAttributes( + Id, + Version, + name: Name, + assemblyName: AssemblyName, + language: Language, + compilationOutputFilePaths: default, + checksumAlgorithm: Text.SourceHashAlgorithms.Default, + defaultNamespace: DefaultNamespace, + filePath: FilePath, + outputFilePath: OutputFilePath, + isSubmission: IsSubmission), CompilationOptions, ParseOptions, - Documents.Where(d => !d.IsSourceGenerated).Select(d => d.ToDocumentInfo()), + documents: Documents.Where(d => !d.IsSourceGenerated).Select(d => d.ToDocumentInfo()), ProjectReferences, MetadataReferences, AnalyzerReferences, - AdditionalDocuments.Select(d => d.ToDocumentInfo()), - IsSubmission, - HostObjectType) - .WithAnalyzerConfigDocuments(AnalyzerConfigDocuments.Select(d => d.ToDocumentInfo())) - .WithDefaultNamespace(DefaultNamespace); + additionalDocuments: AdditionalDocuments.Select(d => d.ToDocumentInfo()), + analyzerConfigDocuments: AnalyzerConfigDocuments.Select(d => d.ToDocumentInfo()), + HostObjectType); } // It is identical with the internal extension method 'GetDefaultExtension' defined in OutputKind.cs. diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs index 48d8d36bfb28c..f6e930d2623c6 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs @@ -983,13 +983,15 @@ private static Compilation CreateCompilation(TestWorkspace workspace, XElement r private static SyntaxTree CreateSyntaxTree(ParseOptions options, string referencedCode) { + var sourceText = SourceText.From(referencedCode, encoding: null, SourceHashAlgorithms.Default); + if (LanguageNames.CSharp == options.Language) { - return Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(referencedCode, options); + return Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(sourceText, options); } else { - return Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(referencedCode, options); + return Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(sourceText, options); } } diff --git a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs index 10760d3d1b757..0eac21a082303 100644 --- a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs +++ b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs @@ -46,7 +46,8 @@ private SnapshotSourceText(ITextBufferCloneService? textBufferCloneService, ITex _container = container; } - public SnapshotSourceText(ITextBufferCloneService? textBufferCloneService, ITextImage textImage, Encoding? encoding, TextBufferContainer? container) + public SnapshotSourceText(ITextBufferCloneService? textBufferCloneService, ITextImage textImage, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, TextBufferContainer? container) + : base(checksumAlgorithm: checksumAlgorithm) { Contract.ThrowIfNull(textImage); @@ -263,8 +264,8 @@ private static ITextImage RecordReverseMapAndGetImage(ITextSnapshot editorSnapsh /// internal sealed class ClosedSnapshotSourceText : SnapshotSourceText { - public ClosedSnapshotSourceText(ITextBufferCloneService? textBufferCloneService, ITextImage textImage, Encoding? encoding) - : base(textBufferCloneService, textImage, encoding, container: null) + public ClosedSnapshotSourceText(ITextBufferCloneService? textBufferCloneService, ITextImage textImage, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) + : base(textBufferCloneService, textImage, encoding, checksumAlgorithm, container: null) { } } @@ -278,7 +279,7 @@ private class ChangedSourceText : SnapshotSourceText private readonly ITextImage _baseSnapshot; public ChangedSourceText(ITextBufferCloneService? textBufferCloneService, SnapshotSourceText baseText, ITextImage baseSnapshot, ITextImage currentSnapshot) - : base(textBufferCloneService, currentSnapshot, baseText.Encoding, container: null) + : base(textBufferCloneService, currentSnapshot, baseText.Encoding, baseText.ChecksumAlgorithm, container: null) { _baseText = baseText; _baseSnapshot = baseSnapshot; diff --git a/src/EditorFeatures/Text/Extensions.cs b/src/EditorFeatures/Text/Extensions.cs index c0abe4ace4d07..a4227135273b8 100644 --- a/src/EditorFeatures/Text/Extensions.cs +++ b/src/EditorFeatures/Text/Extensions.cs @@ -43,8 +43,8 @@ public static SourceText AsText(this ITextSnapshot textSnapshot) return SnapshotSourceText.From(textBufferCloneServiceOpt, textSnapshot); } - internal static SourceText AsRoslynText(this ITextSnapshot textSnapshot, ITextBufferCloneService textBufferCloneServiceOpt, Encoding? encoding) - => new SnapshotSourceText.ClosedSnapshotSourceText(textBufferCloneServiceOpt, ((ITextSnapshot2)textSnapshot).TextImage, encoding); + internal static SourceText AsRoslynText(this ITextSnapshot textSnapshot, ITextBufferCloneService textBufferCloneServiceOpt, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) + => new SnapshotSourceText.ClosedSnapshotSourceText(textBufferCloneServiceOpt, ((ITextSnapshot2)textSnapshot).TextImage, encoding, checksumAlgorithm); /// /// Gets the workspace corresponding to the text buffer. diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs index 22a3d620715de..89d7721cd3d96 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs @@ -58,7 +58,7 @@ internal static class SyntaxHelpers string expr, DiagnosticBag diagnostics) { - var text = SourceText.From(expr); + var text = SourceText.From(expr, encoding: null, SourceHashAlgorithms.Default); var expression = ParseDebuggerExpressionInternal(text, consumeFullText: true); // We're creating a SyntaxTree for just the RHS so that the Diagnostic spans for parse errors // will be correct (with respect to the original input text). If we ever expose a SemanticModel @@ -72,7 +72,7 @@ internal static class SyntaxHelpers // Any Diagnostic spans produced in binding will be offset by the length of the "target" expression text. // If we want to support live squiggles in debugger windows, SemanticModel, etc, we'll want to address this. - var targetSyntax = ParseDebuggerExpressionInternal(SourceText.From(target), consumeFullText: true); + var targetSyntax = ParseDebuggerExpressionInternal(SourceText.From(target, encoding: null, SourceHashAlgorithms.Default), consumeFullText: true); Debug.Assert(!targetSyntax.GetDiagnostics().Any(), "The target of an assignment should never contain Diagnostics if we're being allowed to assign to it in the debugger."); var assignment = InternalSyntax.SyntaxFactory.AssignmentExpression( @@ -80,7 +80,7 @@ internal static class SyntaxHelpers targetSyntax, InternalSyntax.SyntaxFactory.Token(SyntaxKind.EqualsToken), expression); - return assignment.MakeDebuggerExpression(SourceText.From(assignment.ToString())); + return assignment.MakeDebuggerExpression(SourceText.From(assignment.ToString(), encoding: null, SourceHashAlgorithms.Default)); } /// @@ -196,7 +196,7 @@ private static bool RemoveSemicolonIfAny(ref string str) private static ExpressionSyntax ParseDebuggerExpression(string text, bool consumeFullText) { - var source = SourceText.From(text); + var source = SourceText.From(text, encoding: null, SourceHashAlgorithms.Default); var expression = ParseDebuggerExpressionInternal(source, consumeFullText); return expression.MakeDebuggerExpression(source); } @@ -214,7 +214,7 @@ private static InternalSyntax.ExpressionSyntax ParseDebuggerExpressionInternal(S private static StatementSyntax ParseDebuggerStatement(string text) { - var source = SourceText.From(text); + var source = SourceText.From(text, encoding: null, SourceHashAlgorithms.Default); using var lexer = new InternalSyntax.Lexer(source, PreviewParseOptions); using var parser = new InternalSyntax.LanguageParser(lexer, oldTree: null, changes: null, lexerMode: InternalSyntax.LexerMode.DebuggerSyntax); diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SyntaxHelpers.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SyntaxHelpers.vb index 1a8649bef1419..48b1319c59f5c 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SyntaxHelpers.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SyntaxHelpers.vb @@ -5,6 +5,7 @@ Imports System.Collections.ObjectModel Imports System.Runtime.CompilerServices Imports System.Runtime.InteropServices +Imports System.Text Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text @@ -34,12 +35,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Friend Function ParseAssignment(target As String, expr As String, diagnostics As DiagnosticBag) As AssignmentStatementSyntax - Dim text = SourceText.From(expr) + Dim text = SourceText.From(expr, encoding:=Nothing, SourceHashAlgorithms.Default) Dim expression = SyntaxHelpers.ParseDebuggerExpressionInternal(text, consumeFullText:=True) ' We're creating a SyntaxTree for just the RHS so that the Diagnostic spans for parse errors ' will be correct (with respect to the original input text). If we ever expose a SemanticModel ' for debugger expressions, we should use this SyntaxTree. - Dim syntaxTree = expression.CreateSyntaxTree() + Dim syntaxTree = CreateSyntaxTree(expression, text) diagnostics.AddRange(syntaxTree.GetDiagnostics()) If diagnostics.HasAnyErrors Then @@ -48,7 +49,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ' Any Diagnostic spans produced in binding will be offset by the length of the "target" expression text. ' If we want to support live squiggles in debugger windows, SemanticModel, etc, we'll want to address this. - Dim targetSyntax = SyntaxHelpers.ParseDebuggerExpressionInternal(SourceText.From(target), consumeFullText:=True) + Dim targetText = SourceText.From(target, encoding:=Nothing, SourceHashAlgorithms.Default) + Dim targetSyntax = SyntaxHelpers.ParseDebuggerExpressionInternal(targetText, consumeFullText:=True) Debug.Assert(Not targetSyntax.GetDiagnostics().Any(), "The target of an assignment should never contain Diagnostics if we're being allowed to assign to it in the debugger.") Dim assignment = InternalSyntax.SyntaxFactory.SimpleAssignmentStatement( @@ -56,7 +58,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator New InternalSyntax.PunctuationSyntax(SyntaxKind.EqualsToken, "=", Nothing, Nothing), expression) - syntaxTree = assignment.MakeDebuggerStatementContext().CreateSyntaxTree() + Dim assignmentText = SourceText.From(assignment.ToString(), encoding:=Nothing, SourceHashAlgorithms.Default) + syntaxTree = CreateSyntaxTree(assignment.MakeDebuggerStatementContext(), assignmentText) Return DirectCast(syntaxTree.GetDebuggerStatement(), AssignmentStatementSyntax) End Function @@ -142,16 +145,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ''' ''' Parse a debugger expression (e.g. possibly including pseudo-variables). ''' - ''' The input string + ''' The input string ''' ''' It would be better if this method returned ExpressionStatementSyntax, but this is the best we can do for ''' the time being due to issues in the binder resolving ambiguities between invocations and array access. ''' - Friend Function ParseDebuggerExpression(text As String, consumeFullText As Boolean) As PrintStatementSyntax - Dim expression = ParseDebuggerExpressionInternal(SourceText.From(text), consumeFullText) + Friend Function ParseDebuggerExpression(source As String, consumeFullText As Boolean) As PrintStatementSyntax + Dim text = SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default) + Dim expression = ParseDebuggerExpressionInternal(text, consumeFullText) Dim statement = InternalSyntax.SyntaxFactory.PrintStatement( New InternalSyntax.PunctuationSyntax(SyntaxKind.QuestionToken, "?", Nothing, Nothing), expression) - Dim syntaxTree = statement.MakeDebuggerStatementContext().CreateSyntaxTree() + Dim syntaxTree = CreateSyntaxTree(statement.MakeDebuggerStatementContext(), text) Return DirectCast(syntaxTree.GetDebuggerStatement(), PrintStatementSyntax) End Function @@ -166,21 +170,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator End Using End Function - Private Function ParseDebuggerStatement(text As String) As StatementSyntax - Using scanner As New InternalSyntax.Scanner(SourceText.From(text), ParseOptions, isScanningForExpressionCompiler:=True) ' NOTE: Default options should be enough + Private Function ParseDebuggerStatement(source As String) As StatementSyntax + Dim text = SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default) + Using scanner As New InternalSyntax.Scanner(text, ParseOptions, isScanningForExpressionCompiler:=True) ' NOTE: Default options should be enough Using p = New InternalSyntax.Parser(scanner) p.GetNextToken() Dim node = p.ParseStatementInMethodBody() node = p.ConsumeUnexpectedTokens(node) - Dim syntaxTree = node.MakeDebuggerStatementContext().CreateSyntaxTree() + Dim syntaxTree = CreateSyntaxTree(node.MakeDebuggerStatementContext(), text) Return syntaxTree.GetDebuggerStatement() End Using End Using End Function - - Private Function CreateSyntaxTree(root As InternalSyntax.VisualBasicSyntaxNode) As SyntaxTree - Return VisualBasicSyntaxTree.Create(DirectCast(root.CreateRed(Nothing, 0), VisualBasicSyntaxNode), ParseOptions) + Private Function CreateSyntaxTree(root As InternalSyntax.VisualBasicSyntaxNode, text As SourceText) As SyntaxTree + Return VisualBasicSyntaxTree.CreateForDebugger(DirectCast(root.CreateRed(Nothing, 0), VisualBasicSyntaxNode), text, ParseOptions) End Function diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs index 021de0fe2cc7f..a2d1ef502c60e 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs @@ -305,7 +305,7 @@ private async Task ConfigureAsync() { // Project has no solution or solution without a file path. // Add analyzer config to just the current project. - return _project.GetOrCreateAnalyzerConfigDocument(analyzerConfigPath); + return GetOrCreateAnalyzerConfigDocument(_project, analyzerConfigPath); } // Otherwise, add analyzer config document to all applicable projects for the current project's solution. @@ -317,7 +317,7 @@ private async Task ConfigureAsync() var project = currentSolution.GetProject(projectId); if (project?.FilePath?.StartsWith(analyzerConfigDirectory) == true) { - var addedAnalyzerConfigDocument = project.GetOrCreateAnalyzerConfigDocument(analyzerConfigPath); + var addedAnalyzerConfigDocument = GetOrCreateAnalyzerConfigDocument(project, analyzerConfigPath); if (addedAnalyzerConfigDocument != null) { analyzerConfigDocument ??= addedAnalyzerConfigDocument; @@ -329,6 +329,24 @@ private async Task ConfigureAsync() return analyzerConfigDocument; } + private static AnalyzerConfigDocument? GetOrCreateAnalyzerConfigDocument(Project project, string analyzerConfigPath) + { + var existingAnalyzerConfigDocument = project.TryGetExistingAnalyzerConfigDocumentAtPath(analyzerConfigPath); + if (existingAnalyzerConfigDocument != null) + { + return existingAnalyzerConfigDocument; + } + + var id = DocumentId.CreateNewId(project.Id); + var documentInfo = DocumentInfo.Create( + id, + name: ".editorconfig", + filePath: analyzerConfigPath); + + var newSolution = project.Solution.AddAnalyzerConfigDocuments(ImmutableArray.Create(documentInfo)); + return newSolution.GetProject(project.Id)?.GetAnalyzerConfigDocument(id); + } + private static ImmutableArray<(string optionName, string currentOptionValue, bool isPerLanguage)> GetCodeStyleOptionValuesForDiagnostic( Diagnostic diagnostic, Project project) diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index 2e461af6354d0..2230f13e062b6 100644 --- a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs +++ b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs @@ -288,13 +288,13 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel _solution = _solution.AddDocument(DocumentInfo.Create( documentId, name: document.Name, - folders: document.Folders, sourceCodeKind: document.SourceCodeKind, + folders: document.Folders, loader: TextLoader.From(TextAndVersion.Create(sourceText, sourceTextVersion, document.Name)), filePath: document.FilePath, - isGenerated: document.State.Attributes.IsGenerated, - designTimeOnly: document.State.Attributes.DesignTimeOnly, - documentServiceProvider: document.State.Services)); + isGenerated: document.State.Attributes.IsGenerated) + .WithDesignTimeOnly(document.State.Attributes.DesignTimeOnly) + .WithDocumentServiceProvider(document.State.Services)); } if (matchingSourceText != null) diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index 1648359380c0c..f741339d17f8d 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -107,9 +107,9 @@ internal async Task> GetOperationsAsync() return await GetGenerateInNewFileOperationsAsync( namedType, documentName, - null, - true, - null, + folders: null, + areFoldersValidIdentifiers: true, + fullFilePath: null, _semanticDocument.Project, _semanticDocument.Project, isDialog: false).ConfigureAwait(false); @@ -353,11 +353,12 @@ private async Task> CreateAddDocumentAndUpdateU // TODO(cyrusn): make sure documentId is unique. var documentId = DocumentId.CreateNewId(projectToBeUpdated.Id, documentName); - var updatedSolution = projectToBeUpdated.Solution.AddDocument(DocumentInfo.Create( - documentId, - documentName, - containers, - sourceCodeKind)); + var updatedSolution = projectToBeUpdated.Solution.AddDocument( + DocumentInfo.Create( + documentId, + documentName, + containers, + sourceCodeKind)); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(documentId, root, PreservationMode.PreserveIdentity); diff --git a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs index 62dfa0e2d7122..02a054c63e290 100644 --- a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs @@ -279,7 +279,7 @@ private bool RemoveDocumentFromWorkspace(Workspace workspace, MetadataAsSourceGe var documentId = _openedDocumentIds.GetValueOrDefault(fileInfo); Contract.ThrowIfNull(documentId); - workspace.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding)); + workspace.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding, MetadataAsSourceGeneratedFileInfo.ChecksumAlgorithm)); workspace.OnProjectRemoved(documentId.ProjectId); _openedDocumentIds = _openedDocumentIds.RemoveKey(fileInfo); diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index ebf796543ad33..a088026c38196 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -8,6 +8,7 @@ using System.Text; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MetadataAsSource { @@ -45,7 +46,9 @@ public MetadataAsSourceGeneratedFileInfo(string rootPath, Workspace sourceWorksp this.TemporaryFilePath = Path.Combine(rootPath, directoryName, topLevelNamedType.Name + extension); } + // TODO: public static Encoding Encoding => Encoding.UTF8; + public static SourceHashAlgorithm ChecksumAlgorithm => SourceHashAlgorithms.Default; /// /// Creates a ProjectInfo to represent the fake project created for metadata as source documents. @@ -70,26 +73,32 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work ? string.Format(@"[assembly: System.Reflection.AssemblyVersion(""{0}"")]", AssemblyIdentity.Version) : string.Format(@"", AssemblyIdentity.Version); - var assemblyInfoSourceTextContainer = SourceText.From(assemblyInfoString, Encoding).Container; + var assemblyInfoSourceTextContainer = SourceText.From(assemblyInfoString, Encoding, ChecksumAlgorithm).Container; var assemblyInfoDocument = DocumentInfo.Create( assemblyInfoDocumentId, assemblyInfoFileName, - loader: TextLoader.From(assemblyInfoSourceTextContainer, VersionStamp.Default)); + loader: TextLoader.From(assemblyInfoSourceTextContainer, VersionStamp.Default), + filePath: null, + isGenerated: true).WithDesignTimeOnly(true); var generatedDocumentId = DocumentId.CreateNewId(projectId); var generatedDocument = DocumentInfo.Create( generatedDocumentId, Path.GetFileName(TemporaryFilePath), + loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding, ChecksumAlgorithm) : null); filePath: TemporaryFilePath, - loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding) : null); + isGenerated: true).WithDesignTimeOnly(true); var projectInfo = ProjectInfo.Create( - projectId, - VersionStamp.Default, - name: AssemblyIdentity.Name, - assemblyName: AssemblyIdentity.Name, - language: LanguageName, + new ProjectInfo.ProjectAttributes( + id: projectId, + version: VersionStamp.Default, + name: AssemblyIdentity.Name, + assemblyName: AssemblyIdentity.Name, + language: LanguageName, + compilationOutputFilePaths: default, + checksumAlgorithm: ChecksumAlgorithm), compilationOptions: compilationOptions, parseOptions: _parseOptions, documents: new[] { assemblyInfoDocument, generatedDocument }, diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 661fa8469e3e2..abd367ee55ef3 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -119,8 +119,8 @@ + - diff --git a/src/Features/Core/Portable/PdbSourceDocument/DocumentDebugInfoReader.cs b/src/Features/Core/Portable/PdbSourceDocument/DocumentDebugInfoReader.cs index b135115bba2ff..fb211d896b4c7 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/DocumentDebugInfoReader.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/DocumentDebugInfoReader.cs @@ -8,6 +8,7 @@ using System.Reflection.PortableExecutable; using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; using Microsoft.SourceLink.Tools; namespace Microsoft.CodeAnalysis.PdbSourceDocument diff --git a/src/Features/Core/Portable/PdbSourceDocument/IPdbSourceDocumentLoaderService.cs b/src/Features/Core/Portable/PdbSourceDocument/IPdbSourceDocumentLoaderService.cs index caa34093c4bcc..4ebfe3dcc1a5a 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/IPdbSourceDocumentLoaderService.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/IPdbSourceDocumentLoaderService.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.PdbSourceDocument { @@ -16,6 +17,7 @@ internal interface IPdbSourceDocumentLoaderService /// The path to the source file on disk /// Localized description of where the file came from, for the document tab, eg. Source Link, Embedded, On Disk /// The text loader to use + /// Algorithm to use for content checksum. /// Whether the source files came from a remote location, and therefore their existence should be used to indicate that future requests can wait longer - internal sealed record SourceFileInfo(string FilePath, string SourceDescription, TextLoader Loader, bool FromRemoteLocation); + internal sealed record SourceFileInfo(string FilePath, string SourceDescription, TextLoader Loader, SourceHashAlgorithm ChecksumAlgorithm, bool FromRemoteLocation); } diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentLoaderService.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentLoaderService.cs index 8101b2ff5aed2..6f3d4d6f6d913 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentLoaderService.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentLoaderService.cs @@ -197,14 +197,14 @@ public PdbSourceDocumentLoaderService( { using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete); - var sourceText = SourceText.From(stream, encoding, sourceDocument.HashAlgorithm, throwIfBinaryDetected: true); + var sourceText = SourceText.From(stream, encoding, sourceDocument.ChecksumAlgorithm, throwIfBinaryDetected: true); var fileChecksum = sourceText.GetChecksum(); if (ignoreChecksum || fileChecksum.SequenceEqual(sourceDocument.Checksum)) { var textAndVersion = TextAndVersion.Create(sourceText, VersionStamp.Default, filePath); var textLoader = TextLoader.From(textAndVersion); - return new SourceFileInfo(filePath, sourceDescription, textLoader, fromRemoteLocation); + return new SourceFileInfo(filePath, sourceDescription, textLoader, sourceDocument.ChecksumAlgorithm, fromRemoteLocation); } return null; diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index a605e00539b3c..5b0efb788bf5d 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -6,9 +6,12 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -169,12 +172,15 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( } } + Debug.Assert(!sourceDocuments.IsEmpty); + var checksumAlgorithm = sourceDocuments[0].ChecksumAlgorithm; + Encoding? defaultEncoding = null; - if (pdbCompilationOptions.TryGetValue("default-encoding", out var encodingString)) + if (pdbCompilationOptions.TryGetValue(Cci.CompilationOptionNames.DefaultEncoding, out var encodingString)) { defaultEncoding = Encoding.GetEncoding(encodingString); } - else if (pdbCompilationOptions.TryGetValue("fallback-encoding", out var fallbackEncodingString)) + else if (pdbCompilationOptions.TryGetValue(Cci.CompilationOptionNames.FallbackEncoding, out var fallbackEncodingString)) { defaultEncoding = Encoding.GetEncoding(fallbackEncodingString); } @@ -182,7 +188,7 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( if (!_assemblyToProjectMap.TryGetValue(assemblyName, out var projectId)) { // Get the project info now, so we can dispose the documentDebugInfoReader sooner - var projectInfo = CreateProjectInfo(metadataWorkspace, sourceProject, pdbCompilationOptions, assemblyName, assemblyVersion); + var projectInfo = CreateProjectInfo(metadataWorkspace, sourceProject, pdbCompilationOptions, assemblyName, assemblyVersion, checksumAlgorithm); if (projectInfo is null) return null; @@ -247,11 +253,11 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( return new MetadataAsSourceFile(navigateDocument.FilePath, navigateLocation, documentName, documentTooltip); } - private ProjectInfo? CreateProjectInfo(Workspace workspace, Project project, ImmutableDictionary pdbCompilationOptions, string assemblyName, string assemblyVersion) + private ProjectInfo? CreateProjectInfo(Workspace workspace, Project project, ImmutableDictionary pdbCompilationOptions, string assemblyName, string assemblyVersion, SourceHashAlgorithm checksumAlgorithm) { // First we need the language name in order to get the services // TODO: Find language another way for non portable PDBs: https://github.com/dotnet/roslyn/issues/55834 - if (!pdbCompilationOptions.TryGetValue("language", out var languageName) || languageName is null) + if (!pdbCompilationOptions.TryGetValue(Cci.CompilationOptionNames.Language, out var languageName) || languageName is null) { _logger?.Log(FeaturesResources.Source_code_language_information_was_not_found_in_PDB); return null; @@ -265,11 +271,14 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( var projectId = ProjectId.CreateNewId(); return ProjectInfo.Create( - projectId, - VersionStamp.Default, - name: $"{assemblyName} ({assemblyVersion})", - assemblyName: assemblyName, - language: languageName, + new ProjectInfo.ProjectAttributes( + id: projectId, + version: VersionStamp.Default, + name: $"{assemblyName} ({assemblyVersion})", + assemblyName: assemblyName, + language: languageName, + compilationOutputFilePaths: default, + checksumAlgorithm: checksumAlgorithm), compilationOptions: compilationOptions, parseOptions: parseOptions, metadataReferences: project.MetadataReferences.ToImmutableArray()); // TODO: Read references from PDB info: https://github.com/dotnet/roslyn/issues/55834 @@ -297,9 +306,10 @@ private ImmutableArray CreateDocumentInfos( documents.Add(DocumentInfo.Create( documentId, - Path.GetFileName(info.FilePath), + name: Path.GetFileName(info.FilePath), + loader: info.Loader, filePath: info.FilePath, - loader: info.Loader)); + isGenerated: true).WithDesignTimeOnly(true)); // If we successfully got something from SourceLink for this project then its nice to wait a bit longer // if the user performs subsequent navigation @@ -309,7 +319,7 @@ private ImmutableArray CreateDocumentInfos( } // In order to open documents in VS we need to understand the link from temp file to document and its encoding etc. - _fileToDocumentInfoMap[info.FilePath] = new(documentId, encoding, sourceProject.Id, sourceWorkspace); + _fileToDocumentInfoMap[info.FilePath] = new(documentId, encoding, info.ChecksumAlgorithm, sourceProject.Id, sourceWorkspace); } return documents.ToImmutable(); @@ -341,7 +351,7 @@ public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) { if (_fileToDocumentInfoMap.TryGetValue(filePath, out var info)) { - workspace.OnDocumentClosed(info.DocumentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, filePath, info.Encoding)); + workspace.OnDocumentClosed(info.DocumentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, filePath, info.Encoding, info.ChecksumAlgorithm)); return true; } @@ -395,7 +405,7 @@ public void CleanupGeneratedFiles(Workspace? workspace) } } - internal sealed record SourceDocument(string FilePath, SourceHashAlgorithm HashAlgorithm, ImmutableArray Checksum, byte[]? EmbeddedTextBytes, string? SourceLinkUrl); + internal sealed record SourceDocument(string FilePath, SourceHashAlgorithm ChecksumAlgorithm, ImmutableArray Checksum, byte[]? EmbeddedTextBytes, string? SourceLinkUrl); - internal record struct SourceDocumentInfo(DocumentId DocumentId, Encoding Encoding, ProjectId SourceProjectId, Workspace SourceWorkspace); + internal record struct SourceDocumentInfo(DocumentId DocumentId, Encoding Encoding, SourceHashAlgorithm ChecksumAlgorithm, ProjectId SourceProjectId, Workspace SourceWorkspace); } diff --git a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs index e35a4d8a4ea30..7c14edb1bb04f 100644 --- a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs +++ b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Scripting.Hosting; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Features.Workspaces @@ -18,10 +19,12 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument( string filePath, TextLoader textLoader, LanguageInformation languageInformation, + SourceHashAlgorithm checksumAlgorithm, SolutionServices services, ImmutableArray metadataReferences) { var fileExtension = PathUtilities.GetExtension(filePath); + var fileName = PathUtilities.GetFileName(filePath); var languageServices = services.GetLanguageServices(languageInformation.LanguageName); var compilationOptions = languageServices.GetService()?.GetDefaultCompilationOptions(); @@ -43,30 +46,34 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument( var sourceCodeKind = parseOptions?.Kind ?? SourceCodeKind.Regular; var documentInfo = DocumentInfo.Create( - documentId, - filePath, - sourceCodeKind: sourceCodeKind, + id: documentId, + name: fileName, loader: textLoader, - filePath: filePath); + filePath: filePath, + sourceCodeKind: sourceCodeKind); // The assembly name must be unique for each collection of loose files. Since the name doesn't matter // a random GUID can be used. var assemblyName = Guid.NewGuid().ToString("N"); var projectInfo = ProjectInfo.Create( - projectId, - VersionStamp.Create(), - name: FeaturesResources.Miscellaneous_Files, - assemblyName, - languageInformation.LanguageName, + new ProjectInfo.ProjectAttributes( + id: projectId, + version: VersionStamp.Create(), + name: FeaturesResources.Miscellaneous_Files, + assemblyName: assemblyName, + language: languageInformation.LanguageName, + compilationOutputFilePaths: default, + checksumAlgorithm: checksumAlgorithm, + // Miscellaneous files projects are never fully loaded since, by definition, it won't know + // what the full set of information is except when the file is script code. + hasAllInformation: sourceCodeKind == SourceCodeKind.Script), compilationOptions: compilationOptions, parseOptions: parseOptions, documents: SpecializedCollections.SingletonEnumerable(documentInfo), metadataReferences: metadataReferences); - // Miscellaneous files projects are never fully loaded since, by definition, it won't know - // what the full set of information is except when the file is script code. - return projectInfo.WithHasAllInformation(hasAllInformation: sourceCodeKind == SourceCodeKind.Script); + return projectInfo; } // Do not inline this to avoid loading Microsoft.CodeAnalysis.Scripting unless a script file is opened in the workspace. diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs index ab99fdfb45b1e..601c19ca81560 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs @@ -35,7 +35,9 @@ public Task HandleNotificationAsync(LSP.DidOpenTextDocumentParams request, Reque context.TraceInformation($"didOpen for {request.TextDocument.Uri}"); // Add the document and ensure the text we have matches whats on the client - var sourceText = SourceText.From(request.TextDocument.Text, System.Text.Encoding.UTF8); + // TODO (https://github.com/dotnet/roslyn/issues/63583): + // Create SourceText from binary representation of the document, retrieve encoding from the request and checksum algorithm from the project. + var sourceText = SourceText.From(request.TextDocument.Text, System.Text.Encoding.UTF8, SourceHashAlgorithms.Default); context.StartTracking(request.TextDocument.Uri, sourceText); diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 81f806be96697..0c2d9f8fd8203 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer { @@ -61,7 +62,7 @@ public LspMiscellaneousFilesWorkspace() : base(MefHostServices.DefaultHost, Work var sourceTextLoader = new SourceTextLoader(documentText, uriAbsolutePath); - var projectInfo = MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(uri.AbsolutePath, sourceTextLoader, languageInformation, Services.SolutionServices, ImmutableArray.Empty); + var projectInfo = MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(uri.AbsolutePath, sourceTextLoader, languageInformation, documentText.ChecksumAlgorithm, Services.SolutionServices, ImmutableArray.Empty); OnProjectAdded(projectInfo); var id = projectInfo.Documents.Single().Id; @@ -102,6 +103,15 @@ public SourceTextLoader(SourceText sourceText, string fileUri) _fileUri = fileUri; } + internal override SourceHashAlgorithm ChecksumAlgorithm + => _sourceText.ChecksumAlgorithm; + + internal override string? FilePath + => _fileUri; + + private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) + => throw ExceptionUtilities.Unreachable; // TODO: https://github.com/dotnet/roslyn/issues/63583 + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_sourceText, VersionStamp.Create(), _fileUri)); } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs index 741be2d76d792..26c55cb70ca20 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs @@ -61,12 +61,12 @@ public async Task TestLspUsesWorkspaceInstanceOnChangesAsync() var secondDocumentInitialVersion = await secondDocument.GetSyntaxVersionAsync(CancellationToken.None); // Verify the LSP documents are the same instance as the workspaces documents. - Assert.Equal(testLspServer.TestWorkspace.CurrentSolution.GetDocument(firstDocument.Id), firstDocument); - Assert.Equal(testLspServer.TestWorkspace.CurrentSolution.GetDocument(secondDocument.Id), secondDocument); + Assert.Same(testLspServer.TestWorkspace.CurrentSolution.GetDocument(firstDocument.Id), firstDocument); + Assert.Same(testLspServer.TestWorkspace.CurrentSolution.GetDocument(secondDocument.Id), secondDocument); // Make a text change in one of the opened documents in both LSP and the workspace. await testLspServer.InsertTextAsync(firstDocumentUri, (0, 0, "Some more text")); - await testLspServer.TestWorkspace.ChangeDocumentAsync(firstDocument.Id, SourceText.From($"Some more text{markupOne}", System.Text.Encoding.UTF8)); + await testLspServer.TestWorkspace.ChangeDocumentAsync(firstDocument.Id, SourceText.From($"Some more text{markupOne}", System.Text.Encoding.UTF8, SourceHashAlgorithms.Default)); var (_, firstDocumentWithChange) = await GetLspWorkspaceAndDocumentAsync(firstDocumentUri, testLspServer).ConfigureAwait(false); var (_, secondDocumentUnchanged) = await GetLspWorkspaceAndDocumentAsync(secondDocumentUri, testLspServer).ConfigureAwait(false); @@ -100,7 +100,7 @@ public async Task TestLspHasClosedDocumentChangesAsync() await OpenDocumentAndVerifyLspTextAsync(firstDocumentUri, testLspServer); // Modify a closed document via the workspace. - await testLspServer.TestWorkspace.ChangeDocumentAsync(secondDocument.Id, SourceText.From("Two is now three!", System.Text.Encoding.UTF8)); + await testLspServer.TestWorkspace.ChangeDocumentAsync(secondDocument.Id, SourceText.From("Two is now three!", System.Text.Encoding.UTF8, SourceHashAlgorithms.Default)); // Verify that the LSP solution has the LSP text from the open document. var (_, openedDocument) = await GetLspWorkspaceAndDocumentAsync(firstDocumentUri, testLspServer).ConfigureAwait(false); @@ -174,7 +174,7 @@ public async Task TestLspFindsNewDocumentAsync() // Add a new document to the workspace var newDocumentId = DocumentId.CreateNewId(testLspServer.TestWorkspace.CurrentSolution.ProjectIds[0]); - var newSolution = testLspServer.TestWorkspace.CurrentSolution.AddDocument(newDocumentId, "NewDoc.cs", SourceText.From("New Doc", System.Text.Encoding.UTF8), filePath: @"C:\NewDoc.cs"); + var newSolution = testLspServer.TestWorkspace.CurrentSolution.AddDocument(newDocumentId, "NewDoc.cs", SourceText.From("New Doc", System.Text.Encoding.UTF8, SourceHashAlgorithms.Default), filePath: @"C:\NewDoc.cs"); var newDocumentUri = newSolution.GetRequiredDocument(newDocumentId).GetURI(); await testLspServer.TestWorkspace.ChangeSolutionAsync(newSolution); diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 843415ecff98f..7d7adafff54d3 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Newtonsoft.Json; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator { @@ -90,20 +91,25 @@ public static async Task CreateFromInvocationInfoAsync(Compi var projectId = ProjectId.CreateNewId(invocationInfo.ProjectFilePath); var projectInfo = ProjectInfo.Create( - projectId, - VersionStamp.Default, - name: Path.GetFileNameWithoutExtension(invocationInfo.ProjectFilePath), - assemblyName: parsedCommandLine.CompilationName!, - language: languageName, - filePath: invocationInfo.ProjectFilePath, - outputFilePath: parsedCommandLine.OutputFileName, + new ProjectInfo.ProjectAttributes( + id: projectId, + version: VersionStamp.Default, + name: Path.GetFileNameWithoutExtension(invocationInfo.ProjectFilePath), + assemblyName: parsedCommandLine.CompilationName!, + language: languageName, + compilationOutputFilePaths: default, + checksumAlgorithm: parsedCommandLine.ChecksumAlgorithm, + filePath: invocationInfo.ProjectFilePath, + outputFilePath: parsedCommandLine.OutputFileName), parsedCommandLine.CompilationOptions, parsedCommandLine.ParseOptions, parsedCommandLine.SourceFiles.Select(s => CreateDocumentInfo(unmappedPath: s.Path)), + projectReferences: null, metadataReferences: parsedCommandLine.MetadataReferences.Select(r => MetadataReference.CreateFromFile(mapPath(r.Reference), r.Properties)), additionalDocuments: parsedCommandLine.AdditionalFiles.Select(f => CreateDocumentInfo(unmappedPath: f.Path)), - analyzerReferences: parsedCommandLine.AnalyzerReferences.Select(r => new AnalyzerFileReference(r.FilePath, analyzerLoader))) - .WithAnalyzerConfigDocuments(parsedCommandLine.AnalyzerConfigPaths.Select(CreateDocumentInfo)); + analyzerReferences: parsedCommandLine.AnalyzerReferences.Select(r => new AnalyzerFileReference(r.FilePath, analyzerLoader)), + analyzerConfigDocuments: parsedCommandLine.AnalyzerConfigPaths.Select(CreateDocumentInfo), + hostObjectType: null); var solution = workspace.CurrentSolution.AddProject(projectInfo); var compilation = await solution.GetRequiredProject(projectId).GetRequiredCompilationAsync(CancellationToken.None); @@ -119,7 +125,7 @@ DocumentInfo CreateDocumentInfo(string unmappedPath) DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, - loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding)); + loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding, parsedCommandLine.ChecksumAlgorithm))); } } diff --git a/src/Scripting/CSharp/CSharpScript.cs b/src/Scripting/CSharp/CSharpScript.cs index 09df113dbf08c..2e993b87a2c03 100644 --- a/src/Scripting/CSharp/CSharpScript.cs +++ b/src/Scripting/CSharp/CSharpScript.cs @@ -34,7 +34,7 @@ public static Script Create(string code, ScriptOptions options = null, Typ { if (code == null) throw new ArgumentNullException(nameof(code)); - return Script.CreateInitialScript(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding), options, globalsType, assemblyLoader); + return Script.CreateInitialScript(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding, SourceHashAlgorithms.Default), options, globalsType, assemblyLoader); } /// diff --git a/src/Scripting/CoreTestUtilities/TestCompilationFactory.cs b/src/Scripting/CoreTestUtilities/TestCompilationFactory.cs index 0e7bb7c2c582b..b48f3ddb82ca7 100644 --- a/src/Scripting/CoreTestUtilities/TestCompilationFactory.cs +++ b/src/Scripting/CoreTestUtilities/TestCompilationFactory.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; using Basic.Reference.Assemblies; @@ -20,7 +21,7 @@ internal static Compilation CreateCSharpCompilationWithCorlib(string source, str { return CSharpCompilation.Create( assemblyName ?? Guid.NewGuid().ToString(), - new[] { CSharp.SyntaxFactory.ParseSyntaxTree(source) }, + new[] { CSharp.SyntaxFactory.ParseSyntaxTree(SourceText.From(source, encoding: null, SourceHashAlgorithms.Default)) }, new[] { NetStandard13.SystemRuntime }, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); } @@ -29,7 +30,7 @@ internal static Compilation CreateVisualBasicCompilationWithCorlib(string source { return VisualBasicCompilation.Create( assemblyName ?? Guid.NewGuid().ToString(), - new[] { VisualBasic.SyntaxFactory.ParseSyntaxTree(source) }, + new[] { VisualBasic.SyntaxFactory.ParseSyntaxTree(SourceText.From(source, encoding: null, SourceHashAlgorithms.Default)) }, new[] { NetStandard13.SystemRuntime }, new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); } @@ -38,7 +39,7 @@ internal static Compilation CreateCSharpCompilation(string source, IEnumerable - internal class TempPECompilerService : ICSharpTempPECompilerService + internal sealed class TempPECompilerService : ICSharpTempPECompilerService { private readonly IMetadataService _metadataService; @@ -41,8 +42,8 @@ public int CompileTempPE(string pszOutputFileName, int sourceCount, string[] fil for (var i = 0; i < fileNames.Length; i++) { - // create a parse tree w/o encoding - the tree won't be used to emit PDBs - trees.Add(SyntaxFactory.ParseSyntaxTree(fileContents[i], parsedArguments.ParseOptions, fileNames[i])); + var sourceText = SourceText.From(fileContents[i], parsedArguments.Encoding, parsedArguments.ChecksumAlgorithm); + trees.Add(SyntaxFactory.ParseSyntaxTree(sourceText, parsedArguments.ParseOptions, fileNames[i])); } // TODO (tomat): Revisit compilation options: app.config, strong name, search paths, etc? (bug #869604) diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 47a498f3e13d3..3de6038f002c0 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -326,8 +326,14 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy var documentId = DocumentId.CreateNewId(projectToAddTo.Id); - var fileLoader = new WorkspaceFileTextLoader(projectToAddTo.Solution.Services, filePath, defaultEncoding: null); - var forkedSolution = projectToAddTo.Solution.AddDocument(DocumentInfo.Create(documentId, filePath, loader: fileLoader, filePath: filePath)); + var fileLoader = new WorkspaceFileTextLoader(filePath, defaultEncoding: null, projectToAddTo.State.ChecksumAlgorithm); + var forkedSolution = projectToAddTo.Solution.AddDocument( + DocumentInfo.Create( + documentId, + name: filePath, + loader: fileLoader, + filePath: filePath)); + var addedDocument = forkedSolution.GetRequiredDocument(documentId); var globalOptions = _componentModel.GetService(); diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index a3ddb4882dcca..50cecb3c2e1c2 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs @@ -13,8 +13,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview { - using Workspace = Microsoft.CodeAnalysis.Workspace; - internal partial class PreviewUpdater { // internal for testing @@ -52,8 +50,11 @@ private sealed class PreviewTextLoader : TextLoader internal PreviewTextLoader(SourceText documentText) => _text = documentText; + private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) + => throw ExceptionUtilities.Unreachable; // checksum alg should never be changed in preview workspace + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); + => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) => TextAndVersion.Create(_text, VersionStamp.Create()); diff --git a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs index 738ba82f0e16d..c27a7e69e5d23 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -292,8 +292,9 @@ private ProjectInfo CreateProjectInfoForDocument(string filePath) var languageInformation = TryGetLanguageInformation(filePath); Contract.ThrowIfNull(languageInformation); - var loader = new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null); - return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, loader, languageInformation, Services.SolutionServices, _metadataReferences); + var checksumAlgorithm = SourceHashAlgorithms.Default; + var fileLoader = new WorkspaceFileTextLoader(filePath, defaultEncoding: null, checksumAlgorithm); + return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, fileLoader, languageInformation, checksumAlgorithm, Services.SolutionServices, _metadataReferences); } private void DetachFromDocument(string moniker) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index bc8f5db48e61a..c8a00d58ca274 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -92,15 +92,14 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta } var documentId = DocumentId.CreateNewId(_project.Id, fullPath); - var textLoader = new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, fullPath, defaultEncoding: null); + var textLoader = new WorkspaceFileTextLoader(fullPath, defaultEncoding: null, _project.ChecksumAlgorithm); var documentInfo = DocumentInfo.Create( documentId, - FileNameUtilities.GetFileName(fullPath), + name: FileNameUtilities.GetFileName(fullPath), folders: folders.IsDefault ? null : folders, sourceCodeKind: sourceCodeKind, loader: textLoader, - filePath: fullPath, - isGenerated: false); + filePath: fullPath); using (_project._gate.DisposableWait()) { @@ -129,7 +128,13 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta return documentId; } - public DocumentId AddTextContainer(SourceTextContainer textContainer, string fullPath, SourceCodeKind sourceCodeKind, ImmutableArray folders, bool designTimeOnly, IDocumentServiceProvider? documentServiceProvider) + public DocumentId AddTextContainer( + SourceTextContainer textContainer, + string fullPath, + SourceCodeKind sourceCodeKind, + ImmutableArray folders, + bool designTimeOnly, + IDocumentServiceProvider? documentServiceProvider) { if (textContainer == null) { @@ -144,10 +149,9 @@ public DocumentId AddTextContainer(SourceTextContainer textContainer, string ful folders: folders.NullToEmpty(), sourceCodeKind: sourceCodeKind, loader: textLoader, - filePath: fullPath, - isGenerated: false, - designTimeOnly: designTimeOnly, - documentServiceProvider: documentServiceProvider); + filePath: fullPath) + .WithDesignTimeOnly(designTimeOnly) + .WithDocumentServiceProvider(documentServiceProvider); using (_project._gate.DisposableWait()) { @@ -473,18 +477,9 @@ public void ProcessDynamicFileChange(string projectSystemFilePath, string worksp _project.Id, _project._filePath, projectSystemFilePath, CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); // Right now we're only supporting dynamic files as actual source files, so it's OK to call GetDocument here - var document = w.CurrentSolution.GetRequiredDocument(documentId); - - var documentInfo = DocumentInfo.Create( - document.Id, - document.Name, - document.Folders, - document.SourceCodeKind, - loader: fileInfo.TextLoader, - document.FilePath, - document.State.Attributes.IsGenerated, - document.State.Attributes.DesignTimeOnly, - documentServiceProvider: fileInfo.DocumentServiceProvider); + var attributes = w.CurrentSolution.GetRequiredDocument(documentId).State.Attributes; + + var documentInfo = new DocumentInfo(attributes, fileInfo.TextLoader, fileInfo.DocumentServiceProvider); w.OnDocumentReloaded(documentInfo); }); @@ -596,9 +591,9 @@ private DocumentInfo CreateDocumentInfoFromFileInfo(DynamicFileInfo fileInfo, Im sourceCodeKind: fileInfo.SourceCodeKind, loader: textLoader, filePath: filePath, - isGenerated: false, - designTimeOnly: true, - documentServiceProvider: documentServiceProvider); + isGenerated: false) + .WithDesignTimeOnly(true) + .WithDocumentServiceProvider(documentServiceProvider); } private sealed class SourceTextLoader : TextLoader diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.cs index 064136e32ce00..f5ac0c08c2192 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.cs @@ -73,6 +73,7 @@ internal sealed partial class VisualStudioProject private string? _filePath; private CompilationOptions? _compilationOptions; private ParseOptions? _parseOptions; + private SourceHashAlgorithm _checksumAlgorithm = SourceHashAlgorithms.Default; private bool _hasAllInformation = true; private string? _compilationOutputAssemblyFilePath; private string? _outputFilePath; @@ -391,6 +392,12 @@ public string DisplayName set => ChangeProjectProperty(ref _displayName, value, s => s.WithProjectName(Id, value)); } + public SourceHashAlgorithm ChecksumAlgorithm + { + get => _checksumAlgorithm; + set => ChangeProjectProperty(ref _checksumAlgorithm, value, s => s.WithProjectChecksumAlgorithm(Id, value)); + } + // internal to match the visibility of the Workspace-level API -- this is something // we use but we haven't made officially public yet. internal bool HasAllInformation diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs index 3b862e404dfac..d64e248e9c681 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.VSTypeScript.Api; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; @@ -111,15 +112,18 @@ await _visualStudioWorkspaceImpl.ApplyChangeToWorkspaceAsync(w => _visualStudioWorkspaceImpl.AddProjectToInternalMaps_NoLock(project, creationInfo.Hierarchy, creationInfo.ProjectGuid, projectSystemName); var projectInfo = ProjectInfo.Create( + new ProjectInfo.ProjectAttributes( id, versionStamp, name: projectSystemName, assemblyName: assemblyName, language: language, + checksumAlgorithm: SourceHashAlgorithms.Default, // will be updated when command line is set + compilationOutputFilePaths: default, // will be updated when command line is set filePath: creationInfo.FilePath, - compilationOptions: creationInfo.CompilationOptions, - parseOptions: creationInfo.ParseOptions) - .WithTelemetryId(creationInfo.ProjectGuid); + telemetryId: creationInfo.ProjectGuid), + compilationOptions: creationInfo.CompilationOptions, + parseOptions: creationInfo.ParseOptions); // If we don't have any projects and this is our first project being added, then we'll create a new SolutionId // and count this as the solution being added so that event is raised. diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectOptionsProcessor.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectOptionsProcessor.cs index f9e2daf2ffe73..838b2d5a98d22 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectOptionsProcessor.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectOptionsProcessor.cs @@ -205,6 +205,7 @@ private void UpdateProjectOptions_NoLock() _project.CompilationOutputAssemblyFilePath = fullOutputFilePath ?? _project.CompilationOutputAssemblyFilePath; _project.ParseOptions = parseOptions; + _project.ChecksumAlgorithm = _commandLineArgumentsForCommandLine.ChecksumAlgorithm; } private void RuleSetFile_UpdatedOnDisk(object sender, EventArgs e) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs index 5e5098f1e4fd6..10b07935c2bcd 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.ComponentModelHost; @@ -318,20 +319,20 @@ private void TryClosingDocumentsForMoniker(string moniker) { if (w.IsDocumentOpen(documentId) && !_workspace._documentsNotFromFiles.Contains(documentId)) { - var loader = new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null); + var solution = w.CurrentSolution; - if (w.CurrentSolution.ContainsDocument(documentId)) + if (solution.GetDocument(documentId) is { } document) { - w.OnDocumentClosed(documentId, loader); + w.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null, document.State.ChecksumAlgorithm)); } - else if (w.CurrentSolution.ContainsAdditionalDocument(documentId)) + else if (solution.GetAdditionalDocument(documentId) is { } additionalDocument) { - w.OnAdditionalDocumentClosed(documentId, loader); + w.OnAdditionalDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null, additionalDocument.State.ChecksumAlgorithm)); } else { - Debug.Assert(w.CurrentSolution.ContainsAnalyzerConfigDocument(documentId)); - w.OnAnalyzerConfigDocumentClosed(documentId, loader); + var analyzerConfigDocument = solution.GetRequiredAnalyzerConfigDocument(documentId); + w.OnAnalyzerConfigDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null, analyzerConfigDocument.State.ChecksumAlgorithm)); } } } diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs index f3eda7acab198..2ac7c7555d8c4 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs @@ -115,7 +115,12 @@ internal ContainedLanguage( documentId = DocumentId.CreateNewId(projectId, $"{nameof(ContainedDocument)}: {filePath}"); // We must jam a document into an existing workspace, which we'll assume is safe to do with OnDocumentAdded - Workspace.OnDocumentAdded(DocumentInfo.Create(documentId, filePath, filePath: filePath)); + Workspace.OnDocumentAdded(DocumentInfo.Create( + documentId, + name: filePath, + loader: null, + filePath: filePath)); + Workspace.OnDocumentOpened(documentId, SubjectBuffer.AsTextContainer()); } diff --git a/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs b/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs index 3eaefaa414286..6183fee07e51d 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs @@ -108,8 +108,9 @@ internal CodeModelProjectCache( } // Check that we know about this file! - var documentId = State.Workspace.CurrentSolution.GetDocumentIdsWithFilePath(filePath).Where(id => id.ProjectId == _projectId).FirstOrDefault(); - if (documentId == null || State.Workspace.CurrentSolution.GetDocument(documentId) == null) + var solution = State.Workspace.CurrentSolution; + var documentId = solution.GetDocumentIdsWithFilePath(filePath).Where(id => id.ProjectId == _projectId).FirstOrDefault(); + if (documentId == null || solution.GetDocument(documentId) == null) { // Matches behavior of native (C#) implementation throw Exceptions.ThrowENotImpl(); diff --git a/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs b/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs index 8dd4dabc659bc..8548009d647de 100644 --- a/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs +++ b/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs @@ -137,12 +137,6 @@ public void SetOptions(string commandLineForOptions) public void SetOptions(ImmutableArray arguments) => _visualStudioProjectOptionsProcessor?.SetCommandLine(arguments); - public string? DefaultNamespace - { - get => _visualStudioProject.DefaultNamespace; - private set => _visualStudioProject.DefaultNamespace = value; - } - public void SetProperty(string name, string? value) { if (name == BuildPropertyNames.RootNamespace) @@ -152,7 +146,7 @@ public void SetProperty(string name, string? value) // use it for their own purpose. // In the future, we might consider officially exposing "default namespace" for VB project // (e.g. through a msbuild property) - DefaultNamespace = value; + _visualStudioProject.DefaultNamespace = value; } else if (name == BuildPropertyNames.MaxSupportedLangVersion) { diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 82159cf63653e..c26a06654968a 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -172,6 +172,7 @@ static Solution SetProjectProperties(Solution solution, int version) .WithProjectOutputRefFilePath(projectId, "OutputRefFilePath" + version) .WithProjectCompilationOutputInfo(projectId, new CompilationOutputInfo("AssemblyPath" + version)) .WithProjectDefaultNamespace(projectId, "DefaultNamespace" + version) + .WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1 + version) .WithHasAllInformation(projectId, (version % 2) != 0) .WithRunAnalyzers(projectId, (version % 2) != 0); } @@ -186,6 +187,7 @@ static void ValidateProperties(Solution solution, int version) Assert.Equal("OutputRefFilePath" + version, project.OutputRefFilePath); Assert.Equal("AssemblyPath" + version, project.CompilationOutputInfo.AssemblyPath); Assert.Equal("DefaultNamespace" + version, project.DefaultNamespace); + Assert.Equal(SourceHashAlgorithm.Sha1 + version, project.State.ChecksumAlgorithm); Assert.Equal((version % 2) != 0, project.State.HasAllInformation); Assert.Equal((version % 2) != 0, project.State.RunAnalyzers); } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs index 1c97ff18ce64f..06b5c97eeac6f 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs @@ -14,6 +14,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects { diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs index 9372dc858b60c..0274c1cbc0563 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LiveShare.LanguageServices; namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects @@ -86,25 +87,25 @@ public async Task> GetRemoteProjectInfosAsync(Cancel private static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files, SolutionServices services) { var projectId = ProjectId.CreateNewId(); - var docInfos = ImmutableArray.CreateBuilder(); + const SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default; - foreach (var file in files) - { - var fileName = Path.GetFileNameWithoutExtension(file); - var docInfo = DocumentInfo.Create(DocumentId.CreateNewId(projectId), - fileName, - filePath: file, - loader: new WorkspaceFileTextLoaderNoException(services, file, defaultEncoding: null)); - docInfos.Add(docInfo); - } + var docInfos = files.SelectAsArray(path => + DocumentInfo.Create( + DocumentId.CreateNewId(projectId), + name: Path.GetFileNameWithoutExtension(path), + loader: new WorkspaceFileTextLoaderNoException(path, defaultEncoding: null, checksumAlgorithm), + filePath: path)); return ProjectInfo.Create( - projectId, - VersionStamp.Create(), - projectName, - projectName, - language, - documents: docInfos.ToImmutable()); + new ProjectInfo.ProjectAttributes( + projectId, + VersionStamp.Create(), + name: projectName, + assemblyName: projectName, + language, + compilationOutputFilePaths: default, + checksumAlgorithm), + documents: docInfos); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs index 984b7d28617a5..0c2ebe8ff0f7a 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs @@ -18,10 +18,11 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects /// This is a FileTextLoader which no-ops if the file is not available on disk. This is the common case for /// Cascade and throwing exceptions slows down GetText operations significantly enough to have visible UX impact. /// +<<<<<<< HEAD:src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs internal sealed class WorkspaceFileTextLoaderNoException : WorkspaceFileTextLoader { - public WorkspaceFileTextLoaderNoException(SolutionServices services, string path, Encoding defaultEncoding) - : base(services, path, defaultEncoding) + public WorkspaceFileTextLoaderNoException(string path, Encoding defaultEncoding, SourceHashAlgorithm checksumAlgorithm) + : base(path, defaultEncoding, checksumAlgorithm) { } @@ -34,5 +35,8 @@ public override Task LoadTextAndVersionAsync(CancellationToken c return base.LoadTextAndVersionAsync(cancellationToken); } + + private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) + => new FileTextLoaderNoException(Path, DefaultEncoding, algorithm); } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index b2284268131dc..67edb3b48f409 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -331,7 +331,16 @@ private Document AddDocumentToProject(string filePath, string language, string p var project = CurrentSolution.Projects.FirstOrDefault(p => p.Name == projectName && p.Language == language); if (project == null) { - var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), projectName, projectName, language); + var projectInfo = ProjectInfo.Create( + new ProjectInfo.ProjectAttributes( + ProjectId.CreateNewId(), + VersionStamp.Create(), + name: projectName, + assemblyName: projectName, + language, + compilationOutputFilePaths: default, + checksumAlgorithm: SourceHashAlgorithms.Default)); + OnProjectAdded(projectInfo); project = CurrentSolution.GetRequiredProject(projectInfo.Id); } @@ -339,7 +348,7 @@ private Document AddDocumentToProject(string filePath, string language, string p var docInfo = DocumentInfo.Create( DocumentId.CreateNewId(project.Id), name: Path.GetFileName(filePath), - loader: new WorkspaceFileTextLoader(project.Solution.Services, filePath, defaultEncoding: null), + loader: new WorkspaceFileTextLoader(filePath, defaultEncoding: null, project.State.ChecksumAlgorithm), filePath: filePath); OnDocumentAdded(docInfo); @@ -374,7 +383,7 @@ public void NotifyOnDocumentClosing(string moniker) // check if the doc is part of the current Roslyn workspace before notifying Roslyn. if (CurrentSolution.ContainsProject(id.ProjectId)) { - OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(CurrentSolution.Services, moniker, defaultEncoding: null)); + OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(moniker, null, SourceHashAlgorithms.Default)); _openedDocs = _openedDocs.Remove(moniker); } } diff --git a/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb b/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb index 86c21ac62dc47..42f2a2a2cba67 100644 --- a/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb +++ b/src/VisualStudio/VisualBasic/Impl/CodeModel/VisualBasicCodeModelService.vb @@ -33,6 +33,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel Partial Friend Class VisualBasicCodeModelService Inherits AbstractCodeModelService + Private Shared ReadOnly s_emptyTree As SyntaxTree = SyntaxFactory.ParseSyntaxTree(SourceText.From("", encoding:=Nothing, SourceHashAlgorithms.Default)) + Private ReadOnly _commitBufferManagerFactory As CommitBufferManagerFactory Friend Sub New( @@ -3650,7 +3652,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel ' I'm a bad person. Dim tree = compilation.SyntaxTrees.FirstOrDefault() If tree Is Nothing Then - tree = SyntaxFactory.ParseSyntaxTree("") + tree = s_emptyTree compilation = compilation.AddSyntaxTrees(tree) End If diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.NodeSyntaxReference.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.NodeSyntaxReference.cs new file mode 100644 index 0000000000000..3df9c51acd2eb --- /dev/null +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.NodeSyntaxReference.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using System; +using System.Threading; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class CSharpSyntaxTreeFactoryServiceFactory + { + private partial class CSharpSyntaxTreeFactoryService + { + internal sealed class NodeSyntaxReference : SyntaxReference + { + private readonly SyntaxNode _node; + + internal NodeSyntaxReference(SyntaxNode node) + => _node = node; + + public override SyntaxTree SyntaxTree + => _node.SyntaxTree; + + public override TextSpan Span + => _node.Span; + + public override SyntaxNode GetSyntax(CancellationToken cancellationToken) + => _node; + } + } + } +} diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs new file mode 100644 index 0000000000000..9673769034bb0 --- /dev/null +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Threading; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class CSharpSyntaxTreeFactoryServiceFactory + { + private partial class CSharpSyntaxTreeFactoryService + { + /// + /// Parsed that creates with given encoding and checksum algorithm. + /// + private sealed class ParsedSyntaxTree : CSharpSyntaxTree + { + private readonly CSharpSyntaxNode _root; + private readonly SourceHashAlgorithm _checksumAlgorithm; + + public override Encoding? Encoding { get; } + public override CSharpParseOptions Options { get; } + public override string FilePath { get; } + + private SourceText? _lazyText; + + public ParsedSyntaxTree(SourceText? lazyText, CSharpSyntaxNode root, CSharpParseOptions options, string filePath, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) + { + _lazyText = lazyText; + _root = CloneNodeAsRoot(root); + _checksumAlgorithm = checksumAlgorithm; + + Encoding = encoding; + Options = options; + FilePath = filePath; + } + + public override SourceText GetText(CancellationToken cancellationToken) + { + if (_lazyText == null) + { + Interlocked.CompareExchange(ref _lazyText, GetRoot(cancellationToken).GetText(Encoding, _checksumAlgorithm), null); + } + + return _lazyText; + } + + public override bool TryGetText([NotNullWhen(true)] out SourceText? text) + { + text = _lazyText; + return text != null; + } + + public override int Length + => _root.FullSpan.Length; + + public override bool HasCompilationUnitRoot + => true; + + public override CSharpSyntaxNode GetRoot(CancellationToken cancellationToken) + => _root; + + public override bool TryGetRoot([NotNullWhen(true)] out CSharpSyntaxNode? root) + { + root = _root; + return true; + } + + public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options) + => (root == _root && options == Options) ? this : new ParsedSyntaxTree(lazyText: null, (CSharpSyntaxNode)root, (CSharpParseOptions)options, FilePath, Encoding, _checksumAlgorithm); + + public override SyntaxTree WithFilePath(string path) + => (path == FilePath) ? this : new ParsedSyntaxTree(_lazyText, _root, Options, path, Encoding, _checksumAlgorithm); + + public override SyntaxReference GetReference(SyntaxNode node) + => new NodeSyntaxReference(node); + } + } + } +} diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs index e941f4008036e..c16809fb0a6a6 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs @@ -58,7 +58,7 @@ internal static SyntaxTree CreateRecoverableTree( ProjectId cacheKey, string filePath, ParseOptions options, - ValueSource text, + ITextAndVersionSource text, Encoding encoding, CompilationUnitSyntax root) { diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs index 5cd6bf1db00d4..37c091825bfca 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs @@ -69,10 +69,10 @@ public override bool OptionsDifferOnlyByPreprocessorDirectives(ParseOptions opti return csharpOptions1.WithPreprocessorSymbols(csharpOptions2.PreprocessorSymbolNames) == csharpOptions2; } - public override SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SyntaxNode root) + public override SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, SyntaxNode root) { options ??= GetDefaultParseOptions(); - return CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions)options, filePath, encoding); + return new ParsedSyntaxTree(lazyText: null, (CSharpSyntaxNode)root, (CSharpParseOptions)options, filePath, encoding, checksumAlgorithm); } public override SyntaxTree ParseSyntaxTree(string filePath, ParseOptions options, SourceText text, CancellationToken cancellationToken) @@ -91,7 +91,7 @@ public override SyntaxTree CreateRecoverableTree( ProjectId cacheKey, string filePath, ParseOptions options, - ValueSource text, + ITextAndVersionSource text, Encoding encoding, SyntaxNode root) { diff --git a/src/Workspaces/Core/MSBuild/MSBuild/Constants/ItemNames.cs b/src/Workspaces/Core/MSBuild/MSBuild/Constants/ItemNames.cs index 2721447be4bc8..175c288e47c0d 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/Constants/ItemNames.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/Constants/ItemNames.cs @@ -17,5 +17,6 @@ internal static class ItemNames public const string Reference = nameof(Reference); public const string ReferencePath = nameof(ReferencePath); public const string VbcCommandLineArgs = nameof(VbcCommandLineArgs); + public const string IntermediateAssembly = nameof(IntermediateAssembly); } } diff --git a/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs b/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs index edf8b2e1607a3..2691c30f7e5e5 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/Constants/PropertyNames.cs @@ -67,5 +67,6 @@ internal static class PropertyNames public const string WarningLevel = nameof(WarningLevel); public const string WarningsAsErrors = nameof(WarningsAsErrors); public const string WarningsNotAsErrors = nameof(WarningsNotAsErrors); + public const string ChecksumAlgorithm = nameof(ChecksumAlgorithm); } } diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index 5a47611921f87..a7d83e16881ae 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.MSBuild.Build; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MSBuild @@ -302,23 +303,17 @@ private Task CreateProjectInfoAsync(ProjectFileInfo projectFileInfo return Task.FromResult( ProjectInfo.Create( - projectId, - version, - projectName, - assemblyName: assemblyName, - language: language, - filePath: projectPath, - outputFilePath: string.Empty, - outputRefFilePath: string.Empty, + new ProjectInfo.ProjectAttributes( + projectId, + version, + name: projectName, + assemblyName: assemblyName, + language: language, + compilationOutputFilePaths: new CompilationOutputInfo(projectFileInfo.IntermediateOutputFilePath), + checksumAlgorithm: SourceHashAlgorithms.Default, + filePath: projectPath), compilationOptions: compilationOptions, - parseOptions: parseOptions, - documents: SpecializedCollections.EmptyEnumerable(), - projectReferences: SpecializedCollections.EmptyEnumerable(), - metadataReferences: SpecializedCollections.EmptyEnumerable(), - analyzerReferences: SpecializedCollections.EmptyEnumerable(), - additionalDocuments: SpecializedCollections.EmptyEnumerable(), - isSubmission: false, - hostObjectType: null)); + parseOptions: parseOptions)); } return DoOperationAndReportProgressAsync(ProjectLoadOperation.Resolve, projectPath, projectFileInfo.TargetFramework, async () => @@ -366,9 +361,9 @@ private Task CreateProjectInfoAsync(ProjectFileInfo projectFileInfo .WithStrongNameProvider(new DesktopStrongNameProvider(commandLineArgs.KeyFileSearchPaths)) .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default); - var documents = CreateDocumentInfos(projectFileInfo.Documents, projectId, commandLineArgs.Encoding); - var additionalDocuments = CreateDocumentInfos(projectFileInfo.AdditionalDocuments, projectId, commandLineArgs.Encoding); - var analyzerConfigDocuments = CreateDocumentInfos(projectFileInfo.AnalyzerConfigDocuments, projectId, commandLineArgs.Encoding); + var documents = CreateDocumentInfos(projectFileInfo.Documents, projectId, commandLineArgs.Encoding, commandLineArgs.ChecksumAlgorithm); + var additionalDocuments = CreateDocumentInfos(projectFileInfo.AdditionalDocuments, projectId, commandLineArgs.Encoding, commandLineArgs.ChecksumAlgorithm); + var analyzerConfigDocuments = CreateDocumentInfos(projectFileInfo.AnalyzerConfigDocuments, projectId, commandLineArgs.Encoding, commandLineArgs.ChecksumAlgorithm); CheckForDuplicateDocuments(documents.Concat(additionalDocuments).Concat(analyzerConfigDocuments), projectPath, projectId); var analyzerReferences = ResolveAnalyzerReferences(commandLineArgs); @@ -376,14 +371,18 @@ private Task CreateProjectInfoAsync(ProjectFileInfo projectFileInfo var resolvedReferences = await ResolveReferencesAsync(projectId, projectFileInfo, commandLineArgs, cancellationToken).ConfigureAwait(false); return ProjectInfo.Create( - projectId, - version, - projectName, - assemblyName, - language, - projectPath, - outputFilePath: projectFileInfo.OutputFilePath, - outputRefFilePath: projectFileInfo.OutputRefFilePath, + new ProjectInfo.ProjectAttributes( + projectId, + version, + projectName, + assemblyName, + language, + compilationOutputFilePaths: new CompilationOutputInfo(projectFileInfo.IntermediateOutputFilePath), + checksumAlgorithm: commandLineArgs.ChecksumAlgorithm, + filePath: projectPath, + outputFilePath: projectFileInfo.OutputFilePath, + outputRefFilePath: projectFileInfo.OutputRefFilePath, + isSubmission: false), compilationOptions: compilationOptions, parseOptions: parseOptions, documents: documents, @@ -391,7 +390,6 @@ private Task CreateProjectInfoAsync(ProjectFileInfo projectFileInfo metadataReferences: resolvedReferences.MetadataReferences, analyzerReferences: analyzerReferences, additionalDocuments: additionalDocuments, - isSubmission: false, hostObjectType: null) .WithDefaultNamespace(projectFileInfo.DefaultNamespace) .WithAnalyzerConfigDocuments(analyzerConfigDocuments) @@ -441,7 +439,7 @@ private IEnumerable ResolveAnalyzerReferences(CommandLineArgu return commandLineArgs.ResolveAnalyzerReferences(analyzerLoader).Distinct(AnalyzerReferencePathComparer.Instance); } - private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) + private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) { var results = ImmutableArray.CreateBuilder(); @@ -454,7 +452,7 @@ private ImmutableArray CreateDocumentInfos(IReadOnlyList public string? FilePath { get; } + /// + /// The path to the intermediate output file this project generates. + /// + public string? IntermediateOutputFilePath { get; } + /// /// The path to the output file this project generates. /// @@ -99,6 +104,7 @@ private ProjectFileInfo( string? filePath, string? outputFilePath, string? outputRefFilePath, + string? intermediateOutputFilePath, string? defaultNamespace, string? targetFramework, ImmutableArray commandLineArgs, @@ -115,6 +121,7 @@ private ProjectFileInfo( this.FilePath = filePath; this.OutputFilePath = outputFilePath; this.OutputRefFilePath = outputRefFilePath; + this.IntermediateOutputFilePath = intermediateOutputFilePath; this.DefaultNamespace = defaultNamespace; this.TargetFramework = targetFramework; this.CommandLineArgs = commandLineArgs; @@ -130,6 +137,7 @@ public static ProjectFileInfo Create( string? filePath, string? outputFilePath, string? outputRefFilePath, + string? intermediateOutputFilePath, string? defaultNamespace, string? targetFramework, ImmutableArray commandLineArgs, @@ -144,6 +152,7 @@ public static ProjectFileInfo Create( filePath, outputFilePath, outputRefFilePath, + intermediateOutputFilePath, defaultNamespace, targetFramework, commandLineArgs, @@ -160,6 +169,7 @@ public static ProjectFileInfo CreateEmpty(string language, string? filePath, Dia filePath, outputFilePath: null, outputRefFilePath: null, + intermediateOutputFilePath: null, defaultNamespace: null, targetFramework: null, commandLineArgs: ImmutableArray.Empty, diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 50e3e8fe7a60e..38be8b9ea5f49 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -33,6 +33,7 @@ + diff --git a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs index a569e00f79810..2ffcdc9b20d03 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs @@ -173,7 +173,7 @@ public static SerializableSourceText Deserialize( } Contract.ThrowIfFalse(kind == SerializationKinds.Bits); - return new SerializableSourceText(SourceTextExtensions.ReadFrom(textService, reader, encoding, cancellationToken)); + return new SerializableSourceText(SourceTextExtensions.ReadFrom(textService, reader, encoding, checksumAlgorithm, cancellationToken)); } } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 84e5de00012ef..187dee6cb5e3d 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -223,11 +223,11 @@ private static void WriteChunksTo(SourceText sourceText, ObjectWriter writer, in } } - public static SourceText ReadFrom(ITextFactoryService textService, ObjectReader reader, Encoding? encoding, CancellationToken cancellationToken) + public static SourceText ReadFrom(ITextFactoryService textService, ObjectReader reader, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { using var textReader = ObjectReaderTextReader.Create(reader); - return textService.CreateText(textReader, encoding, cancellationToken); + return textService.CreateText(textReader, encoding, checksumAlgorithm, cancellationToken); } private class ObjectReaderTextReader : TextReaderWithLength diff --git a/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs b/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs index 837e29ec4a75f..780e7d31f3f98 100644 --- a/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs +++ b/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs @@ -224,7 +224,7 @@ public SourceText ReadText(CancellationToken cancellationToken) using var reader = CreateTextReaderFromTemporaryStorage(stream); // we pass in encoding we got from original source text even if it is null. - return _service._textFactory.CreateText(reader, _encoding, cancellationToken); + return _service._textFactory.CreateText(reader, _encoding, _checksumAlgorithm, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs index cc5cdc6524dbf..4f846b8cd017b 100644 --- a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs @@ -130,7 +130,7 @@ public Document AddDocument(ProjectId projectId, string name, SourceText text) var id = DocumentId.CreateNewId(projectId); var loader = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create())); - return this.AddDocument(DocumentInfo.Create(id, name, loader: loader)); + return AddDocument(DocumentInfo.Create(id, name, loader: loader)); } /// diff --git a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs index 96b391eaf3b48..01963a5228119 100644 --- a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs @@ -123,11 +123,11 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, var id = DocumentId.CreateNewId(projectId, absolutePath); var doc = DocumentInfo.Create( - id: id, - name: name, + id, + name, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, - loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), + loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding, commandLineArguments.ChecksumAlgorithm), filePath: absolutePath); docs.Add(doc); @@ -154,7 +154,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, name: name, folders: folders, sourceCodeKind: SourceCodeKind.Regular, - loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), + loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding, commandLineArguments.ChecksumAlgorithm), filePath: absolutePath); additionalDocs.Add(doc); @@ -170,11 +170,14 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, // TODO (tomat): what should be the assemblyName when compiling a netmodule? Should it be /moduleassemblyname var projectInfo = ProjectInfo.Create( - projectId, - VersionStamp.Create(), - projectName, - assemblyName, - language: language, + new ProjectInfo.ProjectAttributes( + id: projectId, + version: VersionStamp.Create(), + name: projectName, + assemblyName: assemblyName, + language: language, + compilationOutputFilePaths: new CompilationOutputInfo(commandLineArguments.OutputFileName != null ? commandLineArguments.GetOutputFilePath(commandLineArguments.OutputFileName) : null), + checksumAlgorithm: commandLineArguments.ChecksumAlgorithm), compilationOptions: commandLineArguments.CompilationOptions .WithXmlReferenceResolver(xmlFileResolver) .WithAssemblyIdentityComparer(assemblyIdentityComparer) @@ -183,9 +186,12 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, .WithMetadataReferenceResolver(new WorkspaceMetadataFileReferenceResolver(metadataService, new RelativePathResolver(ImmutableArray.Empty, projectDirectory))), parseOptions: commandLineArguments.ParseOptions, documents: docs, - additionalDocuments: additionalDocs, + projectReferences: null, metadataReferences: boundMetadataReferences, - analyzerReferences: boundAnalyzerReferences); + analyzerReferences: boundAnalyzerReferences, + additionalDocuments: additionalDocs, + analyzerConfigDocuments: null, + hostObjectType: null); return projectInfo; } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs index c17f4975a5343..4c574f4bd18e0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs @@ -19,7 +19,7 @@ internal readonly struct SyntaxTreeInfo { public readonly string FilePath; public readonly ParseOptions Options; - public readonly ValueSource TextSource; + public readonly ITextAndVersionSource TextSource; public readonly Encoding Encoding; public readonly int Length; public readonly bool ContainsDirectives; @@ -27,7 +27,7 @@ internal readonly struct SyntaxTreeInfo public SyntaxTreeInfo( string filePath, ParseOptions options, - ValueSource textSource, + ITextAndVersionSource textSource, Encoding encoding, int length, bool containsDirectives) diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs index cea907c5d1622..1f7def063dcbe 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs @@ -38,9 +38,9 @@ public AbstractSyntaxTreeFactoryService(SolutionServices services) public abstract ParseOptions GetDefaultParseOptionsWithLatestLanguageVersion(); public abstract bool OptionsDifferOnlyByPreprocessorDirectives(ParseOptions options1, ParseOptions options2); public abstract ParseOptions TryParsePdbParseOptions(IReadOnlyDictionary metadata); - public abstract SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SyntaxNode root); + public abstract SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, SyntaxNode root); public abstract SyntaxTree ParseSyntaxTree(string filePath, ParseOptions options, SourceText text, CancellationToken cancellationToken); - public abstract SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string filePath, ParseOptions options, ValueSource text, Encoding encoding, SyntaxNode root); + public abstract SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string filePath, ParseOptions options, ITextAndVersionSource text, Encoding encoding, SyntaxNode root); public abstract SyntaxNode DeserializeNodeFrom(Stream stream, CancellationToken cancellationToken); public virtual bool CanCreateRecoverableTree(SyntaxNode root) diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs index d4e0f83d04d95..2089e82cfb222 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs @@ -29,7 +29,7 @@ internal interface ISyntaxTreeFactoryService : ILanguageService bool OptionsDifferOnlyByPreprocessorDirectives(ParseOptions options1, ParseOptions options2); // new tree from root node - SyntaxTree CreateSyntaxTree(string? filePath, ParseOptions options, Encoding? encoding, SyntaxNode root); + SyntaxTree CreateSyntaxTree(string? filePath, ParseOptions options, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, SyntaxNode root); // new tree from text SyntaxTree ParseSyntaxTree(string? filePath, ParseOptions options, SourceText text, CancellationToken cancellationToken); @@ -37,7 +37,7 @@ internal interface ISyntaxTreeFactoryService : ILanguageService bool CanCreateRecoverableTree(SyntaxNode root); // new recoverable tree from root node - SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string? filePath, ParseOptions options, ValueSource text, Encoding? encoding, SyntaxNode root); + SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string? filePath, ParseOptions options, ITextAndVersionSource text, Encoding? encoding, SyntaxNode root); SyntaxNode DeserializeNodeFrom(Stream stream, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs index 16b6f81221427..c9d93d35320cc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs @@ -23,13 +23,14 @@ internal interface ITextFactoryService : IWorkspaceService /// If not specified auto-detect heuristics are used to determine the encoding. If these heuristics fail the decoding is assumed to be the system encoding. /// Note that if the stream starts with Byte Order Mark the value of is ignored. /// + /// Algorithm to calculate content checksum. /// Cancellation token. /// /// The stream content can't be decoded using the specified , or /// is null and the stream appears to be a binary file. /// /// An IO error occurred while reading from the stream. - SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken); + SourceText CreateText(Stream stream, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken); /// /// Creates from a reader with given . @@ -37,8 +38,9 @@ internal interface ITextFactoryService : IWorkspaceService /// The to read the text from. /// Specifies an encoding for the SourceText. /// it could be null. but if null is given, it won't be able to calculate checksum + /// Algorithm to calculate content checksum. /// Cancellation token. - SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken); + SourceText CreateText(TextReader reader, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs index 9643ae18bda1c..ad36c04be9a20 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs @@ -22,23 +22,19 @@ public TextFactoryService() { } - public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken) + public SourceText CreateText(Stream stream, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - return EncodedStringText.Create(stream, defaultEncoding); + return EncodedStringText.Create(stream, defaultEncoding, checksumAlgorithm); } - public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken) + public SourceText CreateText(TextReader reader, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var textReaderWithLength = reader as TextReaderWithLength; - if (textReaderWithLength != null) - { - return SourceText.From(textReaderWithLength, textReaderWithLength.Length, encoding); - } - - return SourceText.From(reader.ReadToEnd(), encoding); + return (reader is TextReaderWithLength textReaderWithLength) ? + SourceText.From(textReaderWithLength, textReaderWithLength.Length, encoding, checksumAlgorithm) : + SourceText.From(reader.ReadToEnd(), encoding, checksumAlgorithm); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs index d2584f640cded..3b5c98fd565f6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs @@ -18,7 +18,7 @@ private AdditionalDocumentState( IDocumentServiceProvider documentServiceProvider, DocumentInfo.DocumentAttributes attributes, SourceText? sourceText, - ValueSource textAndVersionSource) + ITextAndVersionSource textAndVersionSource) : base(solutionServices, documentServiceProvider, attributes, sourceText, textAndVersionSource) { _additionalText = new AdditionalTextWithState(this); @@ -43,7 +43,7 @@ public AdditionalDocumentState( public new AdditionalDocumentState UpdateText(TextAndVersion newTextAndVersion, PreservationMode mode) => (AdditionalDocumentState)base.UpdateText(newTextAndVersion, mode); - protected override TextDocumentState UpdateText(ValueSource newTextSource, PreservationMode mode, bool incremental) + protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSource, PreservationMode mode, bool incremental) { return new AdditionalDocumentState( this.solutionServices, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs index df2bdcd5dc8a0..15554ce2347d7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs @@ -21,7 +21,7 @@ private AnalyzerConfigDocumentState( IDocumentServiceProvider documentServiceProvider, DocumentInfo.DocumentAttributes attributes, SourceText sourceTextOpt, - ValueSource textAndVersionSource) + ITextAndVersionSource textAndVersionSource) : base(solutionServices, documentServiceProvider, attributes, sourceTextOpt, textAndVersionSource) { _analyzerConfigValueSource = CreateAnalyzerConfigValueSource(); @@ -55,7 +55,7 @@ private ValueSource CreateAnalyzerConfigValueSource() public new AnalyzerConfigDocumentState UpdateText(TextAndVersion newTextAndVersion, PreservationMode mode) => (AnalyzerConfigDocumentState)base.UpdateText(newTextAndVersion, mode); - protected override TextDocumentState UpdateText(ValueSource newTextSource, PreservationMode mode, bool incremental) + protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSource, PreservationMode mode, bool incremental) { return new AnalyzerConfigDocumentState( this.solutionServices, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs new file mode 100644 index 0000000000000..441ea920d5f9d --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +/// +/// This value source keeps a strong reference to a value. +/// +internal sealed class ConstantTextAndVersionSource : ConstantValueSource, ITextAndVersionSource +{ + public ConstantTextAndVersionSource(TextAndVersion value) + : base(value) + { + } + + public SourceHashAlgorithm ChecksumAlgorithm + => Value.Text.ChecksumAlgorithm; + + public bool TryGetTextVersion(out VersionStamp version) + { + version = Value.Version; + return true; + } + + public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) + => null; +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 51b8b71b3c7ef..67cfc06717bac 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -19,6 +20,8 @@ namespace Microsoft.CodeAnalysis [DebuggerDisplay("{GetDebuggerDisplay() , nq}")] public sealed class DocumentInfo { + private readonly TextLoader _textLoader; + internal DocumentAttributes Attributes { get; } /// @@ -51,11 +54,6 @@ public sealed class DocumentInfo /// public bool IsGenerated => Attributes.IsGenerated; - /// - /// A loader that can retrieve the document text. - /// - public TextLoader? TextLoader { get; } - /// /// A associated with this document /// @@ -64,13 +62,16 @@ public sealed class DocumentInfo /// /// Create a new instance of a . /// - internal DocumentInfo(DocumentAttributes attributes, TextLoader? loader, IDocumentServiceProvider? documentServiceProvider) + internal DocumentInfo(DocumentAttributes attributes, TextLoader loader, IDocumentServiceProvider? documentServiceProvider) { Attributes = attributes; - TextLoader = loader; + _textLoader = loader; DocumentServiceProvider = documentServiceProvider; } + /// + /// Creates info. + /// public static DocumentInfo Create( DocumentId id, string name, @@ -80,43 +81,30 @@ public static DocumentInfo Create( string? filePath = null, bool isGenerated = false) { - return Create( - id ?? throw new ArgumentNullException(nameof(id)), - name ?? throw new ArgumentNullException(nameof(name)), - PublicContract.ToBoxedImmutableArrayWithNonNullItems(folders, nameof(folders)), - sourceCodeKind, - loader, - filePath, - isGenerated, - designTimeOnly: false, + return new DocumentInfo( + new DocumentAttributes( + id ?? throw new ArgumentNullException(nameof(id)), + name ?? throw new ArgumentNullException(nameof(name)), + PublicContract.ToBoxedImmutableArrayWithNonNullItems(folders, nameof(folders)), + sourceCodeKind, + filePath, + isGenerated, + designTimeOnly: false), + loader ?? NullTextLoader.Default, documentServiceProvider: null); } - internal static DocumentInfo Create( - DocumentId id, - string name, - IReadOnlyList folders, - SourceCodeKind sourceCodeKind, - TextLoader? loader, - string? filePath, - bool isGenerated, - bool designTimeOnly, - IDocumentServiceProvider? documentServiceProvider) - { - return new DocumentInfo(new DocumentAttributes(id, name, folders, sourceCodeKind, filePath, isGenerated, designTimeOnly: designTimeOnly), loader, documentServiceProvider); - } - private DocumentInfo With( DocumentAttributes? attributes = null, - Optional loader = default, + TextLoader? loader = null, Optional documentServiceProvider = default) { var newAttributes = attributes ?? Attributes; - var newLoader = loader.HasValue ? loader.Value : TextLoader; + var newLoader = loader ?? _textLoader; var newDocumentServiceProvider = documentServiceProvider.HasValue ? documentServiceProvider.Value : DocumentServiceProvider; if (newAttributes == Attributes && - newLoader == TextLoader && + newLoader == _textLoader && newDocumentServiceProvider == DocumentServiceProvider) { return this; @@ -125,6 +113,18 @@ private DocumentInfo With( return new DocumentInfo(newAttributes, newLoader, newDocumentServiceProvider); } + /// + /// A loader that can retrieve the document text. + /// + public TextLoader? TextLoader + => _textLoader is NullTextLoader ? null : _textLoader; + + /// + /// Algorithm used for calculating the document content checksum. + /// + internal SourceHashAlgorithm ChecksumAlgorithm + => _textLoader.ChecksumAlgorithm; + public DocumentInfo WithId(DocumentId id) => With(attributes: Attributes.With(id: id ?? throw new ArgumentNullException(nameof(id)))); @@ -141,7 +141,13 @@ public DocumentInfo WithFilePath(string? filePath) => With(attributes: Attributes.With(filePath: filePath)); public DocumentInfo WithTextLoader(TextLoader? loader) - => With(loader: loader); + => With(loader: loader ?? NullTextLoader.Default); + + internal DocumentInfo WithDesignTimeOnly(bool designTimeOnly) + => With(attributes: Attributes.With(designTimeOnly: designTimeOnly)); + + internal DocumentInfo WithDocumentServiceProvider(IDocumentServiceProvider? provider) + => With(documentServiceProvider: new(provider)); private string GetDebuggerDisplay() => (FilePath == null) ? (nameof(Name) + " = " + Name) : (nameof(FilePath) + " = " + FilePath); @@ -247,7 +253,7 @@ public void WriteTo(ObjectWriter writer) writer.WriteString(Name); writer.WriteValue(Folders.ToArray()); - writer.WriteInt32((int)SourceCodeKind); + writer.WriteByte(checked((byte)SourceCodeKind)); writer.WriteString(FilePath); writer.WriteBoolean(IsGenerated); writer.WriteBoolean(DesignTimeOnly); @@ -259,12 +265,12 @@ public static DocumentAttributes ReadFrom(ObjectReader reader) var name = reader.ReadString(); var folders = (string[])reader.ReadValue(); - var sourceCodeKind = reader.ReadInt32(); + var sourceCodeKind = (SourceCodeKind)reader.ReadByte(); var filePath = reader.ReadString(); var isGenerated = reader.ReadBoolean(); var designTimeOnly = reader.ReadBoolean(); - return new DocumentAttributes(documentId, name, folders, (SourceCodeKind)sourceCodeKind, filePath, isGenerated, designTimeOnly); + return new DocumentAttributes(documentId, name, folders, sourceCodeKind, filePath, isGenerated, designTimeOnly); } Checksum IChecksummedObject.Checksum diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index ac8ac5a8ba987..dbe07544fdea2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -39,7 +39,7 @@ protected DocumentState( DocumentInfo.DocumentAttributes attributes, ParseOptions? options, SourceText? sourceText, - ValueSource textSource, + ITextAndVersionSource textSource, ValueSource? treeSource) : base(solutionServices, documentServiceProvider, attributes, sourceText, textSource) { @@ -111,7 +111,7 @@ private static string GetSyntaxTreeFilePath(DocumentInfo.DocumentAttributes info } protected static ValueSource CreateLazyFullyParsedTree( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -125,7 +125,7 @@ protected static ValueSource CreateLazyFullyParsedTree( } private static async Task FullyParseTreeAsync( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -151,7 +151,7 @@ private static async Task FullyParseTreeAsync( } private static TreeAndVersion FullyParseTree( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -177,7 +177,7 @@ private static TreeAndVersion FullyParseTree( } private static TreeAndVersion CreateTreeAndVersion( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -202,12 +202,12 @@ private static TreeAndVersion CreateTreeAndVersion( CheckTree(tree, text); // text version for this document should be unique. use it as a starting point. - return TreeAndVersion.Create(tree, textAndVersion.Version); + return new TreeAndVersion(tree, textAndVersion.Version, text.ChecksumAlgorithm); } private static ValueSource CreateLazyIncrementallyParsedTree( ValueSource oldTreeSource, - ValueSource newTextSource) + ITextAndVersionSource newTextSource) { return new AsyncLazy( c => IncrementallyParseTreeAsync(oldTreeSource, newTextSource, c), @@ -217,7 +217,7 @@ private static ValueSource CreateLazyIncrementallyParsedTree( private static async Task IncrementallyParseTreeAsync( ValueSource oldTreeSource, - ValueSource newTextSource, + ITextAndVersionSource newTextSource, CancellationToken cancellationToken) { try @@ -238,7 +238,7 @@ private static async Task IncrementallyParseTreeAsync( private static TreeAndVersion IncrementallyParseTree( ValueSource oldTreeSource, - ValueSource newTextSource, + ITextAndVersionSource newTextSource, CancellationToken cancellationToken) { try @@ -277,7 +277,7 @@ private static TreeAndVersion MakeNewTreeAndVersion(SyntaxTree oldTree, SourceTe { var topLevelChanged = TopLevelChanged(oldTree, oldText, newTree, newText); var version = topLevelChanged ? newVersion : oldVersion; - return TreeAndVersion.Create(newTree, version); + return new TreeAndVersion(newTree, version, newText.ChecksumAlgorithm); } private const int MaxTextChangeRangeLength = 1024 * 4; @@ -359,6 +359,7 @@ private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocesso _treeSource.TryGetValue(out var existingTreeAndVersion)) { var existingTree = existingTreeAndVersion.Tree; + SyntaxTree? newTree = null; if (existingTree is IRecoverableSyntaxTree recoverableTree && @@ -370,24 +371,24 @@ private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocesso else if (existingTree.TryGetRoot(out var existingRoot) && !existingRoot.ContainsDirectives) { var treeFactory = _languageServices.GetRequiredService(); - newTree = treeFactory.CreateSyntaxTree(FilePath, options, existingTree.Encoding, existingRoot); + newTree = treeFactory.CreateSyntaxTree(FilePath, options, existingTree.Encoding, existingTreeAndVersion.ChecksumAlgorithm, existingRoot); } if (newTree is not null) - newTreeSource = new ConstantValueSource(TreeAndVersion.Create(newTree, existingTreeAndVersion.Version)); + newTreeSource = new ConstantValueSource(new TreeAndVersion(newTree, existingTreeAndVersion.Version, existingTreeAndVersion.ChecksumAlgorithm)); } // If we weren't able to reuse in a smart way, just reparse newTreeSource ??= CreateLazyFullyParsedTree( - TextAndVersionSource, - Id.ProjectId, - GetSyntaxTreeFilePath(Attributes), - options, - _languageServices); + TextAndVersionSource, + Id.ProjectId, + GetSyntaxTreeFilePath(Attributes), + options, + _languageServices); return new DocumentState( LanguageServices, - solutionServices, + LanguageServices.WorkspaceServices, Services, Attributes.With(sourceCodeKind: options.Kind), options, @@ -420,7 +421,7 @@ private DocumentState UpdateAttributes(DocumentInfo.DocumentAttributes attribute return new DocumentState( _languageServices, - solutionServices, + LanguageServices.WorkspaceServices, Services, attributes, _options, @@ -455,13 +456,26 @@ public DocumentState UpdateFilePath(string? filePath) newTreeSource); } + public DocumentState UpdateChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + { + var newTextSource = TextAndVersionSource.TryUpdateChecksumAlgorithm(checksumAlgorithm); + if (newTextSource == null || newTextSource == TextAndVersionSource) + { + return this; + } + + // Changing the checksum algorithm does not change the tree nodes but the tree itself needs an update. + // Incremental update should be able to reuse the entire root node. + return (DocumentState)UpdateText(newTextSource, PreservationMode.PreserveValue, incremental: true); + } + public new DocumentState UpdateText(SourceText newText, PreservationMode mode) => (DocumentState)base.UpdateText(newText, mode); public new DocumentState UpdateText(TextAndVersion newTextAndVersion, PreservationMode mode) => (DocumentState)base.UpdateText(newTextAndVersion, mode); - protected override TextDocumentState UpdateText(ValueSource newTextSource, PreservationMode mode, bool incremental) + protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSource, PreservationMode mode, bool incremental) { ValueSource? newTreeSource; @@ -508,6 +522,8 @@ internal DocumentState UpdateText(TextLoader loader, SourceText? text, Preservat return documentState; } + Debug.Assert(text.ChecksumAlgorithm == loader.ChecksumAlgorithm); + return new DocumentState( LanguageServices, solutionServices, @@ -552,7 +568,8 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) var filePath = GetSyntaxTreeFilePath(Attributes); Contract.ThrowIfNull(_options); - var (text, tree) = CreateRecoverableTextAndTree(newRoot, filePath, newTextVersion, newTreeVersion, encoding, Attributes, _options, syntaxTreeFactory, mode); + var (text, treeAndVersion) = CreateRecoverableTextAndTree(newRoot, filePath, newTextVersion, newTreeVersion, encoding, ChecksumAlgorithm, Attributes, _options, syntaxTreeFactory, mode); + Debug.Assert(treeAndVersion.ChecksumAlgorithm == ChecksumAlgorithm); return new DocumentState( LanguageServices, @@ -562,7 +579,7 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) _options, sourceText: null, textSource: text, - treeSource: new ConstantValueSource(tree)); + treeSource: new ConstantValueSource(treeAndVersion)); } private VersionStamp GetNewTreeVersionForUpdatedTree(SyntaxNode newRoot, VersionStamp newTextVersion, PreservationMode mode) @@ -583,23 +600,24 @@ private VersionStamp GetNewTreeVersionForUpdatedTree(SyntaxNode newRoot, Version } // use static method so we don't capture references to this - private static (ValueSource, TreeAndVersion) CreateRecoverableTextAndTree( + private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndTree( SyntaxNode newRoot, string filePath, VersionStamp textVersion, VersionStamp treeVersion, Encoding? encoding, + SourceHashAlgorithm checksumAlgorithm, DocumentInfo.DocumentAttributes attributes, ParseOptions options, ISyntaxTreeFactoryService factory, PreservationMode mode) { SyntaxTree tree; - ValueSource lazyTextAndVersion; + ITextAndVersionSource lazyTextAndVersion; if (mode == PreservationMode.PreserveIdentity || !factory.CanCreateRecoverableTree(newRoot)) { - tree = factory.CreateSyntaxTree(filePath, options, encoding, newRoot); + tree = factory.CreateSyntaxTree(GetSyntaxTreeFilePath(attributes), options, encoding, checksumAlgorithm, newRoot); // its okay to use a strong cached AsyncLazy here because the compiler layer SyntaxTree will also keep the text alive once its built. lazyTextAndVersion = new TreeTextSource( @@ -608,7 +626,8 @@ private static (ValueSource, TreeAndVersion) CreateRecoverableTe tree.GetText, cacheResult: true), textVersion, - filePath); + filePath, + checksumAlgorithm); } else { @@ -623,16 +642,17 @@ private static (ValueSource, TreeAndVersion) CreateRecoverableTe new WeaklyCachedValueSource( new AsyncLazy( // Build text from root, so recoverable tree won't cycle. - async cancellationToken => (await tree.GetRootAsync(cancellationToken).ConfigureAwait(false)).GetText(encoding), - cancellationToken => tree.GetRoot(cancellationToken).GetText(encoding), + async cancellationToken => (await tree.GetRootAsync(cancellationToken).ConfigureAwait(false)).GetText(encoding, checksumAlgorithm), + cancellationToken => tree.GetRoot(cancellationToken).GetText(encoding, checksumAlgorithm), cacheResult: false)), textVersion, - filePath); + filePath, + checksumAlgorithm); tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, filePath, options, lazyTextAndVersion, encoding, newRoot); } - return (lazyTextAndVersion, TreeAndVersion.Create(tree, treeVersion)); + return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion, checksumAlgorithm)); } internal override Task GetLoadDiagnosticAsync(CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs index 9d60f5c34a30f..0153e9da0a61c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs @@ -2,8 +2,7 @@ // 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.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; @@ -16,34 +15,37 @@ internal partial class DocumentState /// /// A source for constructed from an syntax tree. /// - private sealed class TreeTextSource : ValueSource, ITextVersionable + private sealed class TreeTextSource : ValueSource, ITextAndVersionSource, ITextVersionable { - private readonly ValueSource _lazyText; + private readonly ValueSource _textSource; private readonly VersionStamp _version; private readonly string _filePath; - public TreeTextSource(ValueSource text, VersionStamp version, string filePath) + public SourceHashAlgorithm ChecksumAlgorithm { get; } + + public TreeTextSource(ValueSource textSource, VersionStamp version, string filePath, SourceHashAlgorithm checksumAlgorithm) { - _lazyText = text; + _textSource = textSource; _version = version; _filePath = filePath; + ChecksumAlgorithm = checksumAlgorithm; } public override async Task GetValueAsync(CancellationToken cancellationToken = default) { - var text = await _lazyText.GetValueAsync(cancellationToken).ConfigureAwait(false); + var text = await _textSource.GetValueAsync(cancellationToken).ConfigureAwait(false); return TextAndVersion.Create(text, _version, _filePath); } public override TextAndVersion GetValue(CancellationToken cancellationToken = default) { - var text = _lazyText.GetValue(cancellationToken); + var text = _textSource.GetValue(cancellationToken); return TextAndVersion.Create(text, _version, _filePath); } - public override bool TryGetValue(out TextAndVersion value) + public override bool TryGetValue([NotNullWhen(true)] out TextAndVersion? value) { - if (_lazyText.TryGetValue(out var text)) + if (_textSource.TryGetValue(out var text)) { value = TextAndVersion.Create(text, _version, _filePath); return true; @@ -60,6 +62,9 @@ public bool TryGetTextVersion(out VersionStamp version) version = _version; return version != default; } + + public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) + => null; } } } diff --git a/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs similarity index 87% rename from src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs rename to src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs index 987c9f3950b39..cd046edbedac9 100644 --- a/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs @@ -31,6 +31,11 @@ public class FileTextLoader : TextLoader /// public Encoding? DefaultEncoding { get; } + /// + /// Algorithm used to calculate content checksum. + /// + internal override SourceHashAlgorithm ChecksumAlgorithm { get; } + /// /// Creates a content loader for specified file. /// @@ -43,13 +48,35 @@ public class FileTextLoader : TextLoader /// is null. /// is not an absolute path. public FileTextLoader(string path, Encoding? defaultEncoding) + : this(path, defaultEncoding, SourceHashAlgorithm.Sha1) + { + } + + /// + /// Creates a content loader for specified file. + /// + /// An absolute file path. + /// + /// Specifies an encoding to be used if the actual encoding can't be determined from the stream content (the stream doesn't start with Byte Order Mark). + /// If not specified auto-detect heuristics are used to determine the encoding. + /// Note that if the stream starts with Byte Order Mark the value of is ignored. + /// + /// Algorithm used to calculate content checksum. + /// is null. + /// is not an absolute path. + internal FileTextLoader(string path, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm) { CompilerPathUtilities.RequireAbsolutePath(path, "path"); + Contract.ThrowIfFalse(SourceHashAlgorithms.IsSupportedAlgorithm(checksumAlgorithm)); Path = path; DefaultEncoding = defaultEncoding; + ChecksumAlgorithm = checksumAlgorithm; } + private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) + => new FileTextLoader(Path, DefaultEncoding, algorithm); + /// /// We have this limit on file size to reduce a chance of OOM when user adds massive files to the solution (often by accident). /// The threshold is 100MB which came from some internal data on big files and some discussion. @@ -58,6 +85,7 @@ public FileTextLoader(string path, Encoding? defaultEncoding) internal sealed override string FilePath => Path; +<<<<<<< HEAD:src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs /// /// Creates from . /// @@ -74,6 +102,13 @@ protected virtual SourceText CreateText(Stream stream, CancellationToken cancell #pragma warning disable CS0618 // Type or member is obsolete => CreateText(stream, workspace: null); #pragma warning restore +======= + protected virtual SourceText CreateText(Stream stream, Workspace workspace) + { + var factory = workspace.Services.GetRequiredService(); + return factory.CreateText(stream, DefaultEncoding, ChecksumAlgorithm, CancellationToken.None); + } +>>>>>>> b316947cf73 (Add ChecksumAlgorithm to DocumentAttributes and ProjectAttributes):src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs /// /// Load a text and a version of the document in the workspace. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs new file mode 100644 index 0000000000000..2d1e4731aa7d9 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis; + +internal interface ITextAndVersionSource +{ + bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value); + TextAndVersion GetValue(CancellationToken cancellationToken); + Task GetValueAsync(CancellationToken cancellationToken); + + SourceHashAlgorithm ChecksumAlgorithm { get; } + ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm); +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs new file mode 100644 index 0000000000000..a20ceb64318f4 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +internal sealed class LoadableTextAndVersionSource : ValueSource, ITextAndVersionSource +{ + private readonly AsyncLazy _lazy; + private readonly TextLoader _loader; + private readonly DocumentId _documentId; + private readonly Workspace _workspace; + + public LoadableTextAndVersionSource(TextLoader loader, DocumentId documentId, Workspace workspace, bool cacheResult) + { + _loader = loader; + _documentId = documentId; + _workspace = workspace; + + _lazy = new AsyncLazy(LoadAsync, LoadSynchronously, cacheResult); + } + + private Task LoadAsync(CancellationToken cancellationToken) + => _loader.LoadTextAsync(_workspace, _documentId, cancellationToken); + + private TextAndVersion LoadSynchronously(CancellationToken cancellationToken) + => _loader.LoadTextSynchronously(_workspace, _documentId, cancellationToken); + + public override TextAndVersion GetValue(CancellationToken cancellationToken) + => _lazy.GetValue(cancellationToken); + + public override bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value) + => _lazy.TryGetValue(out value); + + public override Task GetValueAsync(CancellationToken cancellationToken) + => _lazy.GetValueAsync(cancellationToken); + + public TextLoader Loader + => _loader; + + public DocumentId DocumentId + => _documentId; + + public SourceHashAlgorithm ChecksumAlgorithm + => _loader.ChecksumAlgorithm; + + public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) + { + var newLoader = _loader.TryUpdateChecksumAlgorithm(algorithm); + if (newLoader == null) + { + return null; + } + + if (newLoader == _loader) + { + return this; + } + + return new LoadableTextAndVersionSource(newLoader, _documentId, _workspace, _lazy.CacheResult); + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs new file mode 100644 index 0000000000000..2c1d50fbe5e68 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis; + +/// +/// that does not load text. +/// It only carries . +/// +internal sealed class NullTextLoader : TextLoader +{ + public static readonly NullTextLoader Default = new(SourceHashAlgorithm.Sha1); + + internal override SourceHashAlgorithm ChecksumAlgorithm { get; } + + public NullTextLoader(SourceHashAlgorithm checksumAlgorithm) + { + ChecksumAlgorithm = checksumAlgorithm; + } + + private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) + => new NullTextLoader(algorithm); + + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + => throw new NotImplementedException(); +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs index 7932922e1a846..e5e764550992c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs @@ -5,10 +5,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; +using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -82,6 +85,11 @@ public sealed class ProjectInfo /// internal string? DefaultNamespace => Attributes.DefaultNamespace; + /// + /// Algorithm to calculate content checksum for debugging purposes. + /// + internal SourceHashAlgorithm ChecksumAlgorithm => Attributes.ChecksumAlgorithm; + /// /// True if this is a submission project for interactive sessions. /// @@ -219,22 +227,48 @@ public static ProjectInfo Create( Type? hostObjectType = null, string? outputRefFilePath = null) { - return new ProjectInfo( + return Create( new ProjectAttributes( id ?? throw new ArgumentNullException(nameof(id)), version, name ?? throw new ArgumentNullException(nameof(name)), assemblyName ?? throw new ArgumentNullException(nameof(assemblyName)), language ?? throw new ArgumentNullException(nameof(language)), - filePath, - outputFilePath, - outputRefFilePath, compilationOutputFilePaths: default, + checksumAlgorithm: SourceHashAlgorithm.Sha1, defaultNamespace: null, + filePath: filePath, + outputFilePath: outputFilePath, + outputRefFilePath: outputRefFilePath, + telemetryId: default, isSubmission, hasAllInformation: true, - runAnalyzers: true, - telemetryId: default), + runAnalyzers: true), + compilationOptions, + parseOptions, + documents, + projectReferences, + metadataReferences, + analyzerReferences, + additionalDocuments, + analyzerConfigDocuments: SpecializedCollections.EmptyBoxedImmutableArray(), + hostObjectType); + } + + internal static ProjectInfo Create( + ProjectAttributes attributes, + CompilationOptions? compilationOptions = null, + ParseOptions? parseOptions = null, + IEnumerable? documents = null, + IEnumerable? projectReferences = null, + IEnumerable? metadataReferences = null, + IEnumerable? analyzerReferences = null, + IEnumerable? additionalDocuments = null, + IEnumerable? analyzerConfigDocuments = null, + Type? hostObjectType = null) + { + return new ProjectInfo( + attributes, compilationOptions, parseOptions, PublicContract.ToBoxedImmutableArrayWithDistinctNonNullItems(documents, nameof(documents)), @@ -242,7 +276,7 @@ public static ProjectInfo Create( PublicContract.ToBoxedImmutableArrayWithDistinctNonNullItems(metadataReferences, nameof(metadataReferences)), PublicContract.ToBoxedImmutableArrayWithDistinctNonNullItems(analyzerReferences, nameof(analyzerReferences)), PublicContract.ToBoxedImmutableArrayWithDistinctNonNullItems(additionalDocuments, nameof(additionalDocuments)), - analyzerConfigDocuments: SpecializedCollections.EmptyBoxedImmutableArray(), + PublicContract.ToBoxedImmutableArrayWithDistinctNonNullItems(analyzerConfigDocuments, nameof(analyzerConfigDocuments)), hostObjectType); } @@ -320,6 +354,9 @@ public ProjectInfo WithCompilationOutputInfo(in CompilationOutputInfo info) public ProjectInfo WithDefaultNamespace(string? defaultNamespace) => With(attributes: Attributes.With(defaultNamespace: defaultNamespace)); + internal ProjectInfo WithChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + => With(attributes: Attributes.With(checksumAlgorithm: checksumAlgorithm)); + internal ProjectInfo WithHasAllInformation(bool hasAllInformation) => With(attributes: Attributes.With(hasAllInformation: hasAllInformation)); @@ -367,9 +404,7 @@ internal sealed class ProjectAttributes : IChecksummedObject, IObjectWritable /// /// Matches names like: Microsoft.CodeAnalysis.Features (netcoreapp3.1) /// - private static readonly Regex s_projectNameAndFlavor = new Regex(@"^(?.*?)\s*\((?.*?)\)$", RegexOptions.Compiled); - - private Checksum? _lazyChecksum; + private static readonly Regex s_projectNameAndFlavor = new(@"^(?.*?)\s*\((?.*?)\)$", RegexOptions.Compiled); /// /// The unique Id of the project. @@ -386,14 +421,6 @@ internal sealed class ProjectAttributes : IChecksummedObject, IObjectWritable /// public string Name { get; } - /// - /// The name and flavor portions of the project broken out. For example, the project - /// Microsoft.CodeAnalysis.Workspace (netcoreapp3.1) would have the name - /// Microsoft.CodeAnalysis.Workspace and the flavor netcoreapp3.1. Values may be null if the name does not contain a flavor. - /// - public (string? name, string? flavor) NameAndFlavor { get; } - /// /// The name of the assembly that this project will create, without file extension. /// , @@ -429,6 +456,11 @@ internal sealed class ProjectAttributes : IChecksummedObject, IObjectWritable /// public string? DefaultNamespace { get; } + /// + /// Algorithm to calculate content checksum for debugging purposes. + /// + public SourceHashAlgorithm ChecksumAlgorithm { get; } + /// /// True if this is a submission project for interactive sessions. /// @@ -451,21 +483,45 @@ internal sealed class ProjectAttributes : IChecksummedObject, IObjectWritable /// public Guid TelemetryId { get; } + private StrongBox<(string?, string?)>? _lazyNameAndFlavor; + private Checksum? _lazyChecksum; + + /// + /// The name and flavor portions of the project broken out. For example, the project + /// Microsoft.CodeAnalysis.Workspace (netcoreapp3.1) would have the name + /// Microsoft.CodeAnalysis.Workspace and the flavor netcoreapp3.1. Values may be null if the name does not contain a flavor. + /// + public (string? name, string? flavor) NameAndFlavor + { + get + { + if (_lazyNameAndFlavor == null) + { + var match = s_projectNameAndFlavor.Match(Name); + _lazyNameAndFlavor = new StrongBox<(string?, string?)>(match.Success ? (match.Groups["name"].Value, match.Groups["flavor"].Value) : default); + } + + return _lazyNameAndFlavor.Value; + } + } + public ProjectAttributes( ProjectId id, VersionStamp version, string name, string assemblyName, string language, - string? filePath, - string? outputFilePath, - string? outputRefFilePath, CompilationOutputInfo compilationOutputFilePaths, - string? defaultNamespace, - bool isSubmission, - bool hasAllInformation, - bool runAnalyzers, - Guid telemetryId) + SourceHashAlgorithm checksumAlgorithm, + string? defaultNamespace = null, + string? filePath = null, + string? outputFilePath = null, + string? outputRefFilePath = null, + Guid telemetryId = default, + bool isSubmission = false, + bool hasAllInformation = true, + bool runAnalyzers = true) { Id = id; Name = name; @@ -478,14 +534,11 @@ public ProjectAttributes( OutputRefFilePath = outputRefFilePath; CompilationOutputInfo = compilationOutputFilePaths; DefaultNamespace = defaultNamespace; + ChecksumAlgorithm = checksumAlgorithm; IsSubmission = isSubmission; HasAllInformation = hasAllInformation; RunAnalyzers = runAnalyzers; TelemetryId = telemetryId; - - var match = s_projectNameAndFlavor.Match(Name); - if (match?.Success == true) - NameAndFlavor = (match.Groups["name"].Value, match.Groups["flavor"].Value); } public ProjectAttributes With( @@ -498,6 +551,7 @@ public ProjectAttributes With( Optional outputRefPath = default, Optional compilationOutputInfo = default, Optional defaultNamespace = default, + Optional checksumAlgorithm = default, Optional isSubmission = default, Optional hasAllInformation = default, Optional runAnalyzers = default, @@ -507,11 +561,12 @@ public ProjectAttributes With( var newName = name ?? Name; var newAssemblyName = assemblyName ?? AssemblyName; var newLanguage = language ?? Language; - var newFilepath = filePath.HasValue ? filePath.Value : FilePath; + var newFilePath = filePath.HasValue ? filePath.Value : FilePath; var newOutputPath = outputPath.HasValue ? outputPath.Value : OutputFilePath; var newOutputRefPath = outputRefPath.HasValue ? outputRefPath.Value : OutputRefFilePath; var newCompilationOutputPaths = compilationOutputInfo.HasValue ? compilationOutputInfo.Value : CompilationOutputInfo; var newDefaultNamespace = defaultNamespace.HasValue ? defaultNamespace.Value : DefaultNamespace; + var newChecksumAlgorithm = checksumAlgorithm.HasValue ? checksumAlgorithm.Value : ChecksumAlgorithm; var newIsSubmission = isSubmission.HasValue ? isSubmission.Value : IsSubmission; var newHasAllInformation = hasAllInformation.HasValue ? hasAllInformation.Value : HasAllInformation; var newRunAnalyzers = runAnalyzers.HasValue ? runAnalyzers.Value : RunAnalyzers; @@ -521,11 +576,12 @@ public ProjectAttributes With( newName == Name && newAssemblyName == AssemblyName && newLanguage == Language && - newFilepath == FilePath && + newFilePath == FilePath && newOutputPath == OutputFilePath && newOutputRefPath == OutputRefFilePath && newCompilationOutputPaths == CompilationOutputInfo && newDefaultNamespace == DefaultNamespace && + newChecksumAlgorithm == ChecksumAlgorithm && newIsSubmission == IsSubmission && newHasAllInformation == HasAllInformation && newRunAnalyzers == RunAnalyzers && @@ -540,15 +596,16 @@ public ProjectAttributes With( newName, newAssemblyName, newLanguage, - newFilepath, - newOutputPath, - newOutputRefPath, newCompilationOutputPaths, - newDefaultNamespace, + newChecksumAlgorithm, + defaultNamespace: newDefaultNamespace, + filePath: newFilePath, + outputFilePath: newOutputPath, + outputRefFilePath: newOutputRefPath, + newTelemetryId, newIsSubmission, newHasAllInformation, - newRunAnalyzers, - newTelemetryId); + newRunAnalyzers); } bool IObjectWritable.ShouldReuseInSerialization => true; @@ -568,6 +625,7 @@ public void WriteTo(ObjectWriter writer) writer.WriteString(OutputRefFilePath); CompilationOutputInfo.WriteTo(writer); writer.WriteString(DefaultNamespace); + writer.WriteByte(checked((byte)ChecksumAlgorithm)); writer.WriteBoolean(IsSubmission); writer.WriteBoolean(HasAllInformation); writer.WriteBoolean(RunAnalyzers); @@ -590,6 +648,7 @@ public static ProjectAttributes ReadFrom(ObjectReader reader) var outputRefFilePath = reader.ReadString(); var compilationOutputFilePaths = CompilationOutputInfo.ReadFrom(reader); var defaultNamespace = reader.ReadString(); + var checksumAlgorithm = (SourceHashAlgorithm)reader.ReadByte(); var isSubmission = reader.ReadBoolean(); var hasAllInformation = reader.ReadBoolean(); var runAnalyzers = reader.ReadBoolean(); @@ -598,18 +657,19 @@ public static ProjectAttributes ReadFrom(ObjectReader reader) return new ProjectAttributes( projectId, VersionStamp.Create(), - name, - assemblyName, - language, - filePath, - outputFilePath, - outputRefFilePath, + name: name, + assemblyName: assemblyName, + language: language, compilationOutputFilePaths, - defaultNamespace, - isSubmission, - hasAllInformation, - runAnalyzers, - telemetryId); + checksumAlgorithm, + defaultNamespace: defaultNamespace, + filePath: filePath, + outputFilePath: outputFilePath, + outputRefFilePath: outputRefFilePath, + telemetryId, + isSubmission: isSubmission, + hasAllInformation: hasAllInformation, + runAnalyzers: runAnalyzers); } Checksum IChecksummedObject.Checksum diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index a5246b53caad1..cb81f81f44ac5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -19,6 +20,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -613,6 +615,9 @@ public async Task GetSemanticVersionAsync(CancellationToken cancel [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] public string? DefaultNamespace => this.ProjectInfo.DefaultNamespace; + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] + public SourceHashAlgorithm ChecksumAlgorithm => this.ProjectInfo.ChecksumAlgorithm; + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] public HostLanguageServices LanguageServices => _languageServices; @@ -686,38 +691,54 @@ private ProjectState With( analyzerConfigSet ?? _lazyAnalyzerConfigOptions); } - private ProjectInfo.ProjectAttributes Attributes + internal ProjectInfo.ProjectAttributes Attributes => ProjectInfo.Attributes; - private ProjectState WithAttributes(ProjectInfo.ProjectAttributes attributes) + private ProjectState WithRawAttributes(ProjectInfo.ProjectAttributes attributes) => With(projectInfo: ProjectInfo.With(attributes: attributes)); + public ProjectState WithAttributes(ProjectInfo.ProjectAttributes attributes) + => (attributes == Attributes) ? this : WithRawAttributes(attributes.With(version: Version.GetNewerVersion())); + public ProjectState WithName(string name) - => (name == Name) ? this : WithAttributes(Attributes.With(name: name, version: Version.GetNewerVersion())); + => (name == Name) ? this : WithRawAttributes(Attributes.With(name: name, version: Version.GetNewerVersion())); public ProjectState WithFilePath(string? filePath) - => (filePath == FilePath) ? this : WithAttributes(Attributes.With(filePath: filePath, version: Version.GetNewerVersion())); + => (filePath == FilePath) ? this : WithRawAttributes(Attributes.With(filePath: filePath, version: Version.GetNewerVersion())); public ProjectState WithAssemblyName(string assemblyName) - => (assemblyName == AssemblyName) ? this : WithAttributes(Attributes.With(assemblyName: assemblyName, version: Version.GetNewerVersion())); + => (assemblyName == AssemblyName) ? this : WithRawAttributes(Attributes.With(assemblyName: assemblyName, version: Version.GetNewerVersion())); public ProjectState WithOutputFilePath(string? outputFilePath) - => (outputFilePath == OutputFilePath) ? this : WithAttributes(Attributes.With(outputPath: outputFilePath, version: Version.GetNewerVersion())); + => (outputFilePath == OutputFilePath) ? this : WithRawAttributes(Attributes.With(outputPath: outputFilePath, version: Version.GetNewerVersion())); public ProjectState WithOutputRefFilePath(string? outputRefFilePath) - => (outputRefFilePath == OutputRefFilePath) ? this : WithAttributes(Attributes.With(outputRefPath: outputRefFilePath, version: Version.GetNewerVersion())); + => (outputRefFilePath == OutputRefFilePath) ? this : WithRawAttributes(Attributes.With(outputRefPath: outputRefFilePath, version: Version.GetNewerVersion())); public ProjectState WithCompilationOutputInfo(in CompilationOutputInfo info) - => (info == CompilationOutputInfo) ? this : WithAttributes(Attributes.With(compilationOutputInfo: info, version: Version.GetNewerVersion())); + => (info == CompilationOutputInfo) ? this : WithRawAttributes(Attributes.With(compilationOutputInfo: info, version: Version.GetNewerVersion())); public ProjectState WithDefaultNamespace(string? defaultNamespace) - => (defaultNamespace == DefaultNamespace) ? this : WithAttributes(Attributes.With(defaultNamespace: defaultNamespace, version: Version.GetNewerVersion())); + => (defaultNamespace == DefaultNamespace) ? this : WithRawAttributes(Attributes.With(defaultNamespace: defaultNamespace, version: Version.GetNewerVersion())); public ProjectState WithHasAllInformation(bool hasAllInformation) - => (hasAllInformation == HasAllInformation) ? this : WithAttributes(Attributes.With(hasAllInformation: hasAllInformation, version: Version.GetNewerVersion())); + => (hasAllInformation == HasAllInformation) ? this : WithRawAttributes(Attributes.With(hasAllInformation: hasAllInformation, version: Version.GetNewerVersion())); public ProjectState WithRunAnalyzers(bool runAnalyzers) - => (runAnalyzers == RunAnalyzers) ? this : WithAttributes(Attributes.With(runAnalyzers: runAnalyzers, version: Version.GetNewerVersion())); + => (runAnalyzers == RunAnalyzers) ? this : WithRawAttributes(Attributes.With(runAnalyzers: runAnalyzers, version: Version.GetNewerVersion())); + + public ProjectState WithChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + { + if (checksumAlgorithm == ChecksumAlgorithm) + { + return this; + } + + // Update checksum algorithm of all documents backed by a text loader that supports reloading the content and calculating a new checksum (e.g. file loader). + return With( + projectInfo: ProjectInfo.WithChecksumAlgorithm(checksumAlgorithm).WithVersion(Version.GetNewerVersion()), + documentStates: DocumentStates.UpdateStates(static (state, checksumAlgorithm) => state.UpdateChecksumAlgorithm(checksumAlgorithm), checksumAlgorithm)); + } public ProjectState WithCompilationOptions(CompilationOptions options) { @@ -744,7 +765,7 @@ public ProjectState WithParseOptions(ParseOptions options) return With( projectInfo: ProjectInfo.WithParseOptions(options).WithVersion(Version.GetNewerVersion()), - documentStates: DocumentStates.UpdateStates((state, options) => state.UpdateParseOptions(options, onlyPreprocessorDirectiveChange), options)); + documentStates: DocumentStates.UpdateStates(static (state, args) => state.UpdateParseOptions(args.options, args.onlyPreprocessorDirectiveChange), (options, onlyPreprocessorDirectiveChange))); } public static bool IsSameLanguage(ProjectState project1, ProjectState project2) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs index fded0834bc902..07a30ce9a2a1e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -16,39 +16,50 @@ namespace Microsoft.CodeAnalysis /// /// A recoverable TextAndVersion source that saves its text to temporary storage. /// - internal sealed class RecoverableTextAndVersion : ValueSource, ITextVersionable + internal sealed class RecoverableTextAndVersion : ValueSource, ITextVersionable, ITextAndVersionSource { - private readonly SolutionServices _services; + private readonly HostWorkspaceServices _services; - // Starts as ValueSource and is replaced with RecoverableText when the TextAndVersion value is requested. + // Starts as ITextAndVersionSource and is replaced with RecoverableText when the TextAndVersion value is requested. // At that point the initial source is no longer referenced and can be garbage collected. private object _initialSourceOrRecoverableText; - public RecoverableTextAndVersion(ValueSource initialSource, SolutionServices services) + public RecoverableTextAndVersion(ITextAndVersionSource initialSource, HostWorkspaceServices services) { _initialSourceOrRecoverableText = initialSource; _services = services; } + private bool TryGetInitialSourceOrRecoverableText([NotNullWhen(true)] out ITextAndVersionSource? source, [NotNullWhen(false)] out RecoverableText? text) + { + // store to local to avoid race: + var sourceOrRecoverableText = _initialSourceOrRecoverableText; + + source = sourceOrRecoverableText as ITextAndVersionSource; + if (source != null) + { + text = null; + return true; + } + + text = (RecoverableText)sourceOrRecoverableText; + return false; + } + public ITemporaryTextStorageInternal? Storage => (_initialSourceOrRecoverableText as RecoverableText)?.Storage; public override bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value) { - // store to local to avoid race: - var sourceOrRecoverableText = _initialSourceOrRecoverableText; - - if (sourceOrRecoverableText is RecoverableText recoverableText) + if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) { - if (recoverableText.TryGetValue(out var text)) - { - value = TextAndVersion.Create(text, recoverableText.Version, recoverableText.LoadDiagnostic); - return true; - } + return source.TryGetValue(out value); } - else + + if (recoverableText.TryGetValue(out var text)) { - return ((ValueSource)sourceOrRecoverableText).TryGetValue(out value); + value = TextAndVersion.Create(text, recoverableText.Version, recoverableText.LoadDiagnostic); + return true; } value = null; @@ -74,12 +85,12 @@ public bool TryGetTextVersion(out VersionStamp version) public override TextAndVersion GetValue(CancellationToken cancellationToken = default) { - if (_initialSourceOrRecoverableText is ValueSource source) + if (_initialSourceOrRecoverableText is ITextAndVersionSource source) { // replace initial source with recovarable text if it hasn't been replaced already: Interlocked.CompareExchange( ref _initialSourceOrRecoverableText, - value: new RecoverableText(source.GetValue(cancellationToken), _services), + value: new RecoverableText(source, source.GetValue(cancellationToken), _services.SolutionServices), comparand: source); } @@ -89,12 +100,12 @@ public override TextAndVersion GetValue(CancellationToken cancellationToken = de public override async Task GetValueAsync(CancellationToken cancellationToken = default) { - if (_initialSourceOrRecoverableText is ValueSource source) + if (_initialSourceOrRecoverableText is ITextAndVersionSource source) { // replace initial source with recovarable text if it hasn't been replaced already: Interlocked.CompareExchange( ref _initialSourceOrRecoverableText, - value: new RecoverableText(await source.GetValueAsync(cancellationToken).ConfigureAwait(false), _services), + value: new RecoverableText(source, await source.GetValueAsync(cancellationToken).ConfigureAwait(false), _services.SolutionServices), comparand: source); } @@ -102,21 +113,60 @@ public override async Task GetValueAsync(CancellationToken cance return recoverableText.ToTextAndVersion(await recoverableText.GetValueAsync(cancellationToken).ConfigureAwait(false)); } + public SourceHashAlgorithm ChecksumAlgorithm + => TryGetInitialSourceOrRecoverableText(out var source, out var text) ? source.ChecksumAlgorithm : text.ChecksumAlgorithm; + + public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) + { + if (ChecksumAlgorithm == algorithm) + { + return this; + } + + // store to local to avoid race: + if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) + { + var newSource = source.TryUpdateChecksumAlgorithm(algorithm); + return (newSource != null) ? new RecoverableTextAndVersion(newSource, _services) : null; + } + + var newFileLoader = recoverableText.FileLoader?.TryUpdateChecksumAlgorithm(algorithm); + if (newFileLoader == null) + { + return null; + } + + Contract.ThrowIfNull(recoverableText.DocumentId); + + return new RecoverableTextAndVersion(new LoadableTextAndVersionSource(newFileLoader, recoverableText.DocumentId, _services.Workspace, cacheResult: false), _services); + } + private sealed class RecoverableText : WeaklyCachedRecoverableValueSource, ITextVersionable { private readonly ITemporaryStorageServiceInternal _storageService; public readonly VersionStamp Version; public readonly Diagnostic? LoadDiagnostic; + public readonly FileTextLoader? FileLoader; + public readonly DocumentId? DocumentId; + public readonly SourceHashAlgorithm ChecksumAlgorithm; public ITemporaryTextStorageInternal? _storage; - public RecoverableText(TextAndVersion textAndVersion, SolutionServices services) + public RecoverableText(ITextAndVersionSource source, TextAndVersion textAndVersion, SolutionServices services) : base(new ConstantValueSource(textAndVersion.Text)) { _storageService = services.GetRequiredService(); Version = textAndVersion.Version; LoadDiagnostic = textAndVersion.LoadDiagnostic; + ChecksumAlgorithm = source.ChecksumAlgorithm; + + // preserve file loader so that we can update checksum algorithm later if necessary + if (source is LoadableTextAndVersionSource { Loader: FileTextLoader fileLoader, DocumentId: var documentId }) + { + FileLoader = fileLoader; + DocumentId = documentId; + } } public TextAndVersion ToTextAndVersion(SourceText text) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index b68de95eb67d8..13a435745f3c0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -433,6 +433,22 @@ public Solution WithProjectDefaultNamespace(ProjectId projectId, string? default return new Solution(newState); } + /// + /// Creates a new solution instance with the project specified updated to have the specified attributes. + /// + internal Solution WithProjectChecksumAlgorithm(ProjectId projectId, SourceHashAlgorithm checksumAlgorithm) + { + CheckContainsProject(projectId); + + var newState = _state.WithProjectChecksumAlgorithm(projectId, checksumAlgorithm); + if (newState == _state) + { + return this; + } + + return new Solution(newState); + } + /// /// Creates a new solution instance with the project specified updated to have the name. /// @@ -550,6 +566,23 @@ internal Solution WithRunAnalyzers(ProjectId projectId, bool runAnalyzers) return new Solution(newState); } + /// + /// Creates a new solution instance with the project specified updated to have the attributes, + /// except for which is auto-incremented. + /// + internal Solution WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes) + { + CheckContainsProject(projectId); + + var newState = _state.WithProjectAttributes(projectId, attributes); + if (newState == _state) + { + return this; + } + + return new Solution(newState); + } + /// /// Creates a new solution instance with the project documents in the order by the specified document ids. /// The specified document ids must be the same as what is already in the project; no adding or removing is allowed. @@ -976,7 +1009,18 @@ private static SourceCodeKind GetSourceCodeKind(ProjectState project) /// document instance defined by its name and text. /// public Solution AddDocument(DocumentId documentId, string name, string text, IEnumerable? folders = null, string? filePath = null) - => this.AddDocument(documentId, name, SourceText.From(text), folders, filePath); + { + if (documentId == null) + throw new ArgumentNullException(nameof(documentId)); + + if (name == null) + throw new ArgumentNullException(nameof(name)); + + var project = GetRequiredProjectState(documentId.ProjectId); + var sourceText = SourceText.From(text, encoding: null, checksumAlgorithm: project.ChecksumAlgorithm); + + return AddDocumentImpl(project, documentId, name, sourceText, PublicContract.ToBoxedImmutableArrayWithNonNullItems(folders, nameof(folders)), filePath, isGenerated: false); + } /// /// Creates a new solution instance with the corresponding project updated to include a new @@ -985,48 +1029,49 @@ public Solution AddDocument(DocumentId documentId, string name, string text, IEn public Solution AddDocument(DocumentId documentId, string name, SourceText text, IEnumerable? folders = null, string? filePath = null, bool isGenerated = false) { if (documentId == null) - { throw new ArgumentNullException(nameof(documentId)); - } if (name == null) - { throw new ArgumentNullException(nameof(name)); - } if (text == null) - { throw new ArgumentNullException(nameof(text)); - } - var project = _state.GetProjectState(documentId.ProjectId); + var project = GetRequiredProjectState(documentId.ProjectId); + return AddDocumentImpl(project, documentId, name, text, PublicContract.ToBoxedImmutableArrayWithNonNullItems(folders, nameof(folders)), filePath, isGenerated); + } - if (project == null) - { - throw new InvalidOperationException(string.Format(WorkspacesResources._0_is_not_part_of_the_workspace, documentId.ProjectId)); - } + /// + /// Creates a new solution instance with the corresponding project updated to include a new + /// document instance defined by its name and root . + /// + public Solution AddDocument(DocumentId documentId, string name, SyntaxNode syntaxRoot, IEnumerable? folders = null, string? filePath = null, bool isGenerated = false, PreservationMode preservationMode = PreservationMode.PreserveValue) + { + if (documentId == null) + throw new ArgumentNullException(nameof(documentId)); - var version = VersionStamp.Create(); - var loader = TextLoader.From(TextAndVersion.Create(text, version, name)); + if (name == null) + throw new ArgumentNullException(nameof(name)); + + if (syntaxRoot == null) + throw new ArgumentNullException(nameof(syntaxRoot)); + + var project = GetRequiredProjectState(documentId.ProjectId); + var sourceText = SourceText.From(string.Empty, encoding: null, project.ChecksumAlgorithm); - var info = DocumentInfo.Create( + return AddDocumentImpl(project, documentId, name, sourceText, PublicContract.ToBoxedImmutableArrayWithNonNullItems(folders, nameof(folders)), filePath, isGenerated). + WithDocumentSyntaxRoot(documentId, syntaxRoot, preservationMode); + } + + private Solution AddDocumentImpl(ProjectState project, DocumentId documentId, string name, SourceText text, IReadOnlyList? folders, string? filePath, bool isGenerated) + => AddDocument(DocumentInfo.Create( documentId, name: name, folders: folders, sourceCodeKind: GetSourceCodeKind(project), - loader: loader, + loader: TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create(), name)), filePath: filePath, - isGenerated: isGenerated); - - return this.AddDocument(info); - } - - /// - /// Creates a new solution instance with the corresponding project updated to include a new - /// document instance defined by its name and root . - /// - public Solution AddDocument(DocumentId documentId, string name, SyntaxNode syntaxRoot, IEnumerable? folders = null, string? filePath = null, bool isGenerated = false, PreservationMode preservationMode = PreservationMode.PreserveValue) - => this.AddDocument(documentId, name, SourceText.From(string.Empty), folders, filePath, isGenerated).WithDocumentSyntaxRoot(documentId, syntaxRoot, preservationMode); + isGenerated: isGenerated)); /// /// Creates a new solution instance with the project updated to include a new document with @@ -1035,35 +1080,22 @@ public Solution AddDocument(DocumentId documentId, string name, SyntaxNode synta public Solution AddDocument(DocumentId documentId, string name, TextLoader loader, IEnumerable? folders = null) { if (documentId == null) - { throw new ArgumentNullException(nameof(documentId)); - } if (name == null) - { throw new ArgumentNullException(nameof(name)); - } if (loader == null) - { throw new ArgumentNullException(nameof(loader)); - } - var project = _state.GetProjectState(documentId.ProjectId); - - if (project == null) - { - throw new InvalidOperationException(string.Format(WorkspacesResources._0_is_not_part_of_the_workspace, documentId.ProjectId)); - } + var project = GetRequiredProjectState(documentId.ProjectId); - var info = DocumentInfo.Create( + return AddDocument(DocumentInfo.Create( documentId, - name: name, - folders: folders, - sourceCodeKind: GetSourceCodeKind(project), - loader: loader); - - return this.AddDocument(info); + name, + folders, + GetSourceCodeKind(project), + loader)); } /// @@ -1165,13 +1197,7 @@ public Solution AddAnalyzerConfigDocument(DocumentId documentId, string name, So private DocumentInfo CreateDocumentInfo(DocumentId documentId, string name, SourceText text, IEnumerable? folders, string? filePath) { - var project = _state.GetProjectState(documentId.ProjectId); - - if (project is null) - { - throw new InvalidOperationException(string.Format(WorkspacesResources._0_is_not_part_of_the_workspace, documentId.ProjectId)); - } - + var project = GetRequiredProjectState(documentId.ProjectId); var version = VersionStamp.Create(); var loader = TextLoader.From(TextAndVersion.Create(text, version, name)); @@ -1184,6 +1210,9 @@ private DocumentInfo CreateDocumentInfo(DocumentId documentId, string name, Sour filePath: filePath); } + private ProjectState GetRequiredProjectState(ProjectId projectId) + => _state.GetProjectState(projectId) ?? throw new InvalidOperationException(string.Format(WorkspacesResources._0_is_not_part_of_the_workspace, projectId)); + /// /// Creates a new Solution instance that contains a new compiler configuration document like a .editorconfig file. /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index e144146590d85..094ba2d106332 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -704,6 +704,22 @@ public SolutionState WithProjectDefaultNamespace(ProjectId projectId, string? de return ForkProject(newProject); } + /// + /// Creates a new solution instance with the project specified updated to have the name. + /// + public SolutionState WithProjectChecksumAlgorithm(ProjectId projectId, SourceHashAlgorithm checksumAlgorithm) + { + var oldProject = GetRequiredProjectState(projectId); + var newProject = oldProject.WithChecksumAlgorithm(checksumAlgorithm); + + if (oldProject == newProject) + { + return this; + } + + return ForkProject(newProject); + } + /// /// Creates a new solution instance with the project specified updated to have the name. /// @@ -815,6 +831,23 @@ public SolutionState WithRunAnalyzers(ProjectId projectId, bool runAnalyzers) return ForkProject(newProject); } + /// + /// Create a new solution instance with the project specified updated to have + /// the specified attributes, except for which is auto-incremented. + /// + public SolutionState WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes) + { + var oldProject = GetRequiredProjectState(projectId); + var newProject = oldProject.WithAttributes(attributes); + + if (oldProject == newProject) + { + return this; + } + + return ForkProject(newProject); + } + /// /// Create a new solution instance with the project specified updated to include /// the specified project references. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index 64b5a064e4feb..6b77f54d88861 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs @@ -22,7 +22,7 @@ public static SourceGeneratedDocumentState Create( HostWorkspaceServices solutionServices) { var textAndVersion = TextAndVersion.Create(generatedSourceText, VersionStamp.Create()); - var textSource = new ConstantValueSource(textAndVersion); + var textSource = new ConstantTextAndVersionSource(textAndVersion); var treeSource = CreateLazyFullyParsedTree( textSource, documentIdentity.DocumentId.ProjectId, @@ -55,7 +55,7 @@ private SourceGeneratedDocumentState( IDocumentServiceProvider? documentServiceProvider, DocumentInfo.DocumentAttributes attributes, ParseOptions options, - ValueSource textSource, + ITextAndVersionSource textSource, ValueSource treeSource) : base(languageServices, solutionServices, documentServiceProvider, attributes, options, sourceText: null, textSource, treeSource) { @@ -65,10 +65,8 @@ private SourceGeneratedDocumentState( // The base allows for parse options to be null for non-C#/VB languages, but we'll always have parse options public new ParseOptions ParseOptions => base.ParseOptions!; - protected override TextDocumentState UpdateText(ValueSource newTextSource, PreservationMode mode, bool incremental) - { - throw new NotSupportedException(WorkspacesResources.The_contents_of_a_SourceGeneratedDocument_may_not_be_changed); - } + protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSource, PreservationMode mode, bool incremental) + => throw new NotSupportedException(WorkspacesResources.The_contents_of_a_SourceGeneratedDocument_may_not_be_changed); public SourceGeneratedDocumentState WithUpdatedGeneratedContent(SourceText sourceText, ParseOptions parseOptions) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index 3c1a4836d573e..3120e58023575 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Threading; @@ -33,7 +34,7 @@ internal partial class TextDocumentState /// only retrieve is asynchronously through . /// protected readonly SourceText? sourceText; - protected ValueSource TextAndVersionSource { get; } + protected ITextAndVersionSource TextAndVersionSource { get; } // Checksums for this solution state private readonly ValueSource _lazyChecksums; @@ -50,11 +51,12 @@ protected TextDocumentState( IDocumentServiceProvider? documentServiceProvider, DocumentInfo.DocumentAttributes attributes, SourceText? sourceText, - ValueSource textAndVersionSource) + ITextAndVersionSource textAndVersionSource) { this.solutionServices = solutionServices; this.sourceText = sourceText; - this.TextAndVersionSource = textAndVersionSource; + + TextAndVersionSource = textAndVersionSource; Attributes = attributes; Services = documentServiceProvider ?? DefaultTextDocumentServiceProvider.Instance; @@ -68,33 +70,30 @@ protected TextDocumentState( } public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) - : this(services, info.DocumentServiceProvider, info.Attributes, sourceText: null, - textAndVersionSource: info.TextLoader != null - ? CreateRecoverableText(info.TextLoader, services.SolutionServices) - : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, Encoding.UTF8), VersionStamp.Default, info.FilePath))) + ? CreateRecoverableText(info.TextLoader, info.Id, services.SolutionServices) + : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, encoding: null, info.ChecksumAlgorithm), VersionStamp.Default, info.FilePath))) { } + public SourceHashAlgorithm ChecksumAlgorithm => TextAndVersionSource.ChecksumAlgorithm; public DocumentId Id => Attributes.Id; public string? FilePath => Attributes.FilePath; public IReadOnlyList Folders => Attributes.Folders; public string Name => Attributes.Name; - protected static ValueSource CreateStrongText(TextAndVersion text) - => new ConstantValueSource(text); + private static ITextAndVersionSource CreateStrongText(TextAndVersion text) + => new ConstantTextAndVersionSource(text); - protected static ValueSource CreateStrongText(TextLoader loader) - { - return new AsyncLazy(loader.LoadTextAsync, loader.LoadTextSynchronously, cacheResult: true); - } + private static ITextAndVersionSource CreateStrongText(TextLoader loader, DocumentId documentId, SolutionServices services) + => new LoadableTextAndVersionSource(loader, documentId, services, cacheResult: true); - protected static ValueSource CreateRecoverableText(TextAndVersion text, SolutionServices services) + private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, HostWorkspaceServices services) { - var result = new RecoverableTextAndVersion(CreateStrongText(text), services); + var result = new RecoverableTextAndVersion(new ConstantTextAndVersionSource(text), services); // This RecoverableTextAndVersion is created directly from a TextAndVersion instance. In its initial state, // the RecoverableTextAndVersion keeps a strong reference to the initial TextAndVersion, and only @@ -108,7 +107,7 @@ protected static ValueSource CreateRecoverableText(TextAndVersio } protected static ValueSource CreateRecoverableText(TextLoader loader, SolutionServices services) - => new RecoverableTextAndVersion(new AsyncLazy(loader.LoadTextAsync, loader.LoadTextSynchronously, cacheResult: false), services); + => new RecoverableTextAndVersion(new LoadableTextAndVersionSource(loader, documentId, services, cacheResult: false), services); public ITemporaryTextStorageInternal? Storage => (TextAndVersionSource as RecoverableTextAndVersion)?.Storage; @@ -203,7 +202,7 @@ public TextDocumentState UpdateText(TextAndVersion newTextAndVersion, Preservati { var newTextSource = mode == PreservationMode.PreserveIdentity ? CreateStrongText(newTextAndVersion) - : CreateRecoverableText(newTextAndVersion, this.solutionServices.SolutionServices); + : CreateRecoverableText(newTextAndVersion, solutionServices); return UpdateText(newTextSource, mode, incremental: true); } @@ -220,16 +219,16 @@ public TextDocumentState UpdateText(TextLoader loader, PreservationMode mode) { // don't blow up on non-text documents. var newTextSource = mode == PreservationMode.PreserveIdentity - ? CreateStrongText(loader) - : CreateRecoverableText(loader, solutionServices.SolutionServices); + ? CreateStrongText(loader, Id, solutionServices) + : CreateRecoverableText(loader, Id, solutionServices); return UpdateText(newTextSource, mode, incremental: false); } - protected virtual TextDocumentState UpdateText(ValueSource newTextSource, PreservationMode mode, bool incremental) + protected virtual TextDocumentState UpdateText(ITextAndVersionSource newTextSource, PreservationMode mode, bool incremental) { return new TextDocumentState( - this.solutionServices, + solutionServices, this.Services, this.Attributes, sourceText: null, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index a4fb2434a711e..edac004a5afe5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -28,6 +28,14 @@ public abstract class TextLoader internal virtual string? FilePath => null; + internal virtual SourceHashAlgorithm ChecksumAlgorithm => SourceHashAlgorithm.Sha1; + + internal TextLoader? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) + => (ChecksumAlgorithm == algorithm) ? this : TryUpdateChecksumAlgorithmImpl(algorithm); + + private protected virtual TextLoader? TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) + => null; + /// /// Load a text and a version of the document. /// @@ -193,6 +201,9 @@ private sealed class TextDocumentLoader : TextLoader internal TextDocumentLoader(TextAndVersion textAndVersion) => _textAndVersion = textAndVersion; + internal override SourceHashAlgorithm ChecksumAlgorithm + => _textAndVersion.Text.ChecksumAlgorithm; + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => Task.FromResult(_textAndVersion); @@ -213,8 +224,11 @@ internal TextContainerLoader(SourceTextContainer container, VersionStamp version _filePath = filePath; } + internal override string? FilePath + => _filePath; + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); + => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) => TextAndVersion.Create(_container.CurrentText, _version, _filePath); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs index 4ddbdf70b6f9e..7cb487b232d9d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis { @@ -21,20 +22,13 @@ internal sealed class TreeAndVersion /// public VersionStamp Version { get; } - private TreeAndVersion(SyntaxTree tree, VersionStamp version) - { - this.Tree = tree; - this.Version = version; - } + public SourceHashAlgorithm ChecksumAlgorithm { get; } - public static TreeAndVersion Create(SyntaxTree tree, VersionStamp version) + public TreeAndVersion(SyntaxTree tree, VersionStamp version, SourceHashAlgorithm checksumAlgorithm) { - if (tree == null) - { - throw new ArgumentNullException(nameof(tree)); - } - - return new TreeAndVersion(tree, version); + Tree = tree; + Version = version; + ChecksumAlgorithm = checksumAlgorithm; } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 2548c9cbe32b7..286d79761735f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -1629,7 +1629,7 @@ private void ApplyChangedDocument( // ApplyDocumentInfoChanged ignores the loader information, so we can pass null for it ApplyDocumentInfoChanged( documentId, - new DocumentInfo(newDoc.State.Attributes, loader: null, documentServiceProvider: newDoc.State.Services)); + new DocumentInfo(newDoc.State.Attributes, NullTextLoader.Default, documentServiceProvider: newDoc.State.Services)); } } @@ -1645,44 +1645,32 @@ private static void CheckNoChanges(Solution oldSolution, Solution newSolution) private static ProjectInfo CreateProjectInfo(Project project) { return ProjectInfo.Create( - project.Id, - VersionStamp.Create(), - project.Name, - project.AssemblyName, - project.Language, - project.FilePath, - project.OutputFilePath, + project.State.Attributes.With(version: VersionStamp.Create()), project.CompilationOptions, project.ParseOptions, project.Documents.Select(CreateDocumentInfoWithText), project.ProjectReferences, project.MetadataReferences, project.AnalyzerReferences, - project.AdditionalDocuments.Select(CreateDocumentInfoWithText), - project.IsSubmission, - project.State.HostObjectType, - project.OutputRefFilePath) - .WithDefaultNamespace(project.DefaultNamespace) - .WithAnalyzerConfigDocuments(project.AnalyzerConfigDocuments.Select(CreateDocumentInfoWithText)); + additionalDocuments: project.AdditionalDocuments.Select(CreateDocumentInfoWithText), + analyzerConfigDocuments: project.AnalyzerConfigDocuments.Select(CreateDocumentInfoWithText), + hostObjectType: project.State.HostObjectType); } private static DocumentInfo CreateDocumentInfoWithText(TextDocument doc) => CreateDocumentInfoWithoutText(doc).WithTextLoader(TextLoader.From(TextAndVersion.Create(doc.GetTextSynchronously(CancellationToken.None), VersionStamp.Create(), doc.FilePath))); internal static DocumentInfo CreateDocumentInfoWithoutText(TextDocument doc) - { - var sourceDoc = doc as Document; - return DocumentInfo.Create( + => DocumentInfo.Create( doc.Id, doc.Name, doc.Folders, - sourceDoc != null ? sourceDoc.SourceCodeKind : SourceCodeKind.Regular, + doc is Document sourceDoc ? sourceDoc.SourceCodeKind : SourceCodeKind.Regular, loader: null, filePath: doc.FilePath, - isGenerated: false, - designTimeOnly: false, - doc.Services); - } + isGenerated: doc.State.Attributes.IsGenerated) + .WithDesignTimeOnly(doc.State.Attributes.DesignTimeOnly) + .WithDocumentServiceProvider(doc.Services); /// /// This method is called during to add a project to the current solution. diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 08c8388203e15..65024c1af3ab7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -474,6 +474,12 @@ public ReuseVersionLoader(DocumentState oldDocumentState, SourceText newText) _newText = newText; } + internal override SourceHashAlgorithm ChecksumAlgorithm + => _newText.ChecksumAlgorithm; + + internal override string? FilePath + => _oldDocumentState.FilePath; + public override async Task LoadTextAndVersionAsync(CancellationToken cancellationToken) { var oldText = await _oldDocumentState.GetTextAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/CoreTest/Simplifier/SimplifierTests.cs b/src/Workspaces/CoreTest/Simplifier/SimplifierTests.cs index 6469f9361a1c7..08d85b61bdc87 100644 --- a/src/Workspaces/CoreTest/Simplifier/SimplifierTests.cs +++ b/src/Workspaces/CoreTest/Simplifier/SimplifierTests.cs @@ -42,7 +42,7 @@ public async Task ExpandAsync_BadArguments() await Assert.ThrowsAsync("document", () => Simplifier.ExpandAsync(token: default, document: null!)); } - [Fact, Obsolete("Obsolete so we can test obsolete API")] + [Fact, Obsolete("Testing obsolete API")] public async Task Expand_BadArguments() { var node = SyntaxFactory.IdentifierName(SyntaxFactory.Identifier("Test")); diff --git a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs index f470c4cc54d52..4522d5f48efbb 100644 --- a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs @@ -9,6 +9,9 @@ using System.IO; using System.Linq; using Roslyn.Test.Utilities; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests @@ -44,6 +47,26 @@ public void Create() Assert.Equal(SourceCodeKind.Script, info.SourceCodeKind); Assert.Same(loader, info.TextLoader); Assert.True(info.IsGenerated); + Assert.Equal(SourceHashAlgorithms.Default, info.ChecksumAlgorithm); + } + + [Fact] + public void Create_NullLoader() + { + var id = DocumentId.CreateNewId(ProjectId.CreateNewId()); + + var info = DocumentInfo.Create( + id, + name: "doc", + sourceCodeKind: SourceCodeKind.Script, + loader: null, + isGenerated: true); + + Assert.Equal(id, info.Id); + Assert.Equal("doc", info.Name); + Assert.Equal(SourceCodeKind.Script, info.SourceCodeKind); + Assert.Null(info.TextLoader); + Assert.True(info.IsGenerated); } [Fact] @@ -80,11 +103,13 @@ public void TestProperties() var projectId = ProjectId.CreateNewId(); var documentId = DocumentId.CreateNewId(projectId); var instance = DocumentInfo.Create(DocumentId.CreateNewId(ProjectId.CreateNewId()), "doc"); + var serviceProvider = (IDocumentServiceProvider)new TestDocumentServiceProvider(); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithId(value), opt => opt.Id, documentId, defaultThrows: true); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithName(value), opt => opt.Name, "New", defaultThrows: true); - SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithSourceCodeKind(value), opt => opt.SourceCodeKind, SourceCodeKind.Script); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithTextLoader(value), opt => opt.TextLoader, (TextLoader)new TestTextLoader("text")); + SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithDesignTimeOnly(value), opt => opt.Attributes.DesignTimeOnly, true); + SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithDocumentServiceProvider(value), opt => opt.DocumentServiceProvider, serviceProvider); SolutionTestHelpers.TestListProperty(instance, (old, value) => old.WithFolders(value), opt => opt.Folders, "folder", allowDuplicates: true); } diff --git a/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs index 22ee39756ca65..cacdf41fffdcb 100644 --- a/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; @@ -189,8 +190,9 @@ public void TestProperties() SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithOutputRefFilePath(value), opt => opt.OutputRefFilePath, "New"); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithCompilationOutputInfo(value), opt => opt.CompilationOutputInfo, new CompilationOutputInfo("NewPath")); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithDefaultNamespace(value), opt => opt.DefaultNamespace, "New"); - SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithHasAllInformation(value), opt => opt.HasAllInformation, true); - SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithRunAnalyzers(value), opt => opt.RunAnalyzers, true); + SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithChecksumAlgorithm(value), opt => opt.ChecksumAlgorithm, SourceHashAlgorithm.None); + SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithHasAllInformation(value), opt => opt.HasAllInformation, false); + SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithRunAnalyzers(value), opt => opt.RunAnalyzers, false); SolutionTestHelpers.TestListProperty(instance, (old, value) => old.WithDocuments(value), opt => opt.Documents, documentInfo, allowDuplicates: false); SolutionTestHelpers.TestListProperty(instance, (old, value) => old.WithAdditionalDocuments(value), opt => opt.AdditionalDocuments, documentInfo, allowDuplicates: false); diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTestHelpers.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTestHelpers.cs index c5b03dfeefee6..c2eb301cc8756 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTestHelpers.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTestHelpers.cs @@ -37,7 +37,7 @@ public static Workspace CreateWorkspaceWithPartialSemanticsAndWeakCompilations() public static void TestProperty(T instance, Func factory, Func getter, TValue validNonDefaultValue, bool defaultThrows = false) where T : class { - Assert.NotEqual(default, validNonDefaultValue); + Assert.NotEqual(getter(instance), validNonDefaultValue); var instanceWithValue = factory(instance, validNonDefaultValue); Assert.Equal(validNonDefaultValue, getter(instanceWithValue)); diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 131038d8a0c9b..dd23321dade89 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -8,10 +8,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Drawing.Drawing2D; using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; +using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -19,7 +20,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; @@ -27,7 +27,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.UnitTests.Persistence; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.VisualStudio.Threading; using Roslyn.Test.Utilities; @@ -36,7 +35,7 @@ using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; using static Microsoft.CodeAnalysis.UnitTests.SolutionTestHelpers; -using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; namespace Microsoft.CodeAnalysis.UnitTests { @@ -56,9 +55,9 @@ private static Workspace CreateWorkspaceWithProjectAndDocuments() Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp) - .AddDocument(DocumentId.CreateNewId(projectId), "goo.cs", "public class Goo { }") - .AddAdditionalDocument(DocumentId.CreateNewId(projectId), "add.txt", "text") - .AddAnalyzerConfigDocument(DocumentId.CreateNewId(projectId), "editorcfg", SourceText.From("config"), filePath: "/a/b"))); + .AddDocument(DocumentId.CreateNewId(projectId), "goo.cs", SourceText.From("public class Goo { }", Encoding.UTF8, SourceHashAlgorithms.Default)) + .AddAdditionalDocument(DocumentId.CreateNewId(projectId), "add.txt", SourceText.From("text", Encoding.UTF8, SourceHashAlgorithms.Default)) + .AddAnalyzerConfigDocument(DocumentId.CreateNewId(projectId), "editorcfg", SourceText.From("config", Encoding.UTF8, SourceHashAlgorithms.Default), filePath: "/a/b"))); return workspace; } @@ -236,7 +235,7 @@ public void WithSourceCodeKind() Assert.Throws(() => solution.WithDocumentSourceCodeKind(s_unrelatedDocumentId, SourceCodeKind.Script)); } - [Fact, Obsolete] + [Fact, Obsolete("Testing obsolete API")] public void WithSourceCodeKind_Obsolete() { using var workspace = CreateWorkspaceWithProjectAndDocuments(); @@ -253,7 +252,11 @@ public void WithDocumentSyntaxRoot() using var workspace = CreateWorkspaceWithProjectAndDocuments(); var solution = workspace.CurrentSolution; var documentId = solution.Projects.Single().DocumentIds.Single(); - var root = CS.SyntaxFactory.ParseSyntaxTree("class NewClass {}").GetRoot(); + + var tree = CS.SyntaxFactory.ParseSyntaxTree("class NewClass {}"); + Assert.Equal(SourceHashAlgorithm.Sha1, tree.GetText().ChecksumAlgorithm); + + var root = tree.GetRoot(); var newSolution1 = solution.WithDocumentSyntaxRoot(documentId, root, PreservationMode.PreserveIdentity); Assert.True(newSolution1.GetDocument(documentId)!.TryGetSyntaxRoot(out var actualRoot)); @@ -295,10 +298,13 @@ public void WithDocumentText_SourceText() using var workspace = CreateWorkspaceWithProjectAndDocuments(); var solution = workspace.CurrentSolution; var documentId = solution.Projects.Single().DocumentIds.Single(); - var text = SourceText.From("new text"); + + var text = SourceText.From("new text", encoding: null, SourceHashAlgorithm.Sha1); var newSolution1 = solution.WithDocumentText(documentId, text, PreservationMode.PreserveIdentity); - Assert.True(newSolution1.GetDocument(documentId)!.TryGetText(out var actualText)); + var newDocument1 = newSolution1.GetRequiredDocument(documentId); + + Assert.True(newDocument1.TryGetText(out var actualText)); Assert.Same(text, actualText); var newSolution2 = newSolution1.WithDocumentText(documentId, text, PreservationMode.PreserveIdentity); @@ -554,7 +560,7 @@ public void WithProjectOutputFilePath() SolutionTestHelpers.TestProperty( solution, (s, value) => s.WithProjectOutputFilePath(projectId, value), - s => s.GetProject(projectId)!.OutputFilePath, + s => s.GetRequiredProject(projectId).OutputFilePath, (string?)path, defaultThrows: false); @@ -577,7 +583,7 @@ public void WithProjectOutputRefFilePath() SolutionTestHelpers.TestProperty( solution, (s, value) => s.WithProjectOutputRefFilePath(projectId, value), - s => s.GetProject(projectId)!.OutputRefFilePath, + s => s.GetRequiredProject(projectId).OutputRefFilePath, (string?)path, defaultThrows: false); @@ -600,7 +606,7 @@ public void WithProjectCompilationOutputInfo() SolutionTestHelpers.TestProperty( solution, (s, value) => s.WithProjectCompilationOutputInfo(projectId, value), - s => s.GetProject(projectId)!.CompilationOutputInfo, + s => s.GetRequiredProject(projectId).CompilationOutputInfo, new CompilationOutputInfo(path), defaultThrows: false); @@ -614,8 +620,8 @@ public void WithProjectDefaultNamespace() var projectId = ProjectId.CreateNewId(); using var workspace = CreateWorkspace(); - var solution = workspace.CurrentSolution - .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp); + var solution = workspace.CurrentSolution. + AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp); // any character is allowed var defaultNamespace = "\0<>a/b/*"; @@ -623,7 +629,7 @@ public void WithProjectDefaultNamespace() SolutionTestHelpers.TestProperty( solution, (s, value) => s.WithProjectDefaultNamespace(projectId, value), - s => s.GetProject(projectId)!.DefaultNamespace, + s => s.GetRequiredProject(projectId).DefaultNamespace, (string?)defaultNamespace, defaultThrows: false); @@ -631,6 +637,88 @@ public void WithProjectDefaultNamespace() Assert.Throws(() => solution.WithProjectDefaultNamespace(ProjectId.CreateNewId(), "x")); } + [Fact] + public void WithProjectChecksumAlgorithm() + { + var projectId = ProjectId.CreateNewId(); + + using var workspace = CreateWorkspace(); + var solution = workspace.CurrentSolution. + AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp); + + SolutionTestHelpers.TestProperty( + solution, + (s, value) => s.WithProjectChecksumAlgorithm(projectId, value), + s => s.GetRequiredProject(projectId).State.ChecksumAlgorithm, + SourceHashAlgorithms.Default, + defaultThrows: false); + } + + [Fact] + public void WithProjectChecksumAlgorithm_DocumentUpdates() + { + var projectId = ProjectId.CreateNewId(); + var documentAId = DocumentId.CreateNewId(projectId); + var documentBId = DocumentId.CreateNewId(projectId); + var documentCId = DocumentId.CreateNewId(projectId); + var documentDId = DocumentId.CreateNewId(projectId); + + var fileD = Temp.CreateFile(); + var bytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true).GetBytes("Text"); + fileD.WriteAllBytes(bytes); + + var sha256 = SHA256.Create(); + var sha1 = SHA1.Create(); + var checksumSHA1 = sha1.ComputeHash(bytes); + var checksumSHA256 = sha256.ComputeHash(bytes); + sha256.Dispose(); + sha1.Dispose(); + + using var workspace = CreateWorkspace(); + + var textLoaderA = new TestTextLoader("class A {}", SourceHashAlgorithm.Sha1); + var textC = SourceText.From("class C {}", encoding: null, checksumAlgorithm: SourceHashAlgorithm.Sha1); + + var solution = workspace.CurrentSolution. + AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); + + solution = solution.AddDocument(documentAId, "a.cs", textLoaderA); + solution = solution.AddDocument(documentBId, "b.cs", "class B {}"); + solution = solution.AddDocument(documentCId, "c.cs", textC); + solution = solution.AddDocument(documentDId, "d.cs", new FileTextLoader(fileD.Path, defaultEncoding: null, SourceHashAlgorithm.Sha1)); + + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentAId).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentBId).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentCId).GetTextSynchronously(default).ChecksumAlgorithm); + + var fileText = solution.GetRequiredDocument(documentDId).GetTextSynchronously(default); + Assert.Equal(SourceHashAlgorithm.Sha1, fileText.ChecksumAlgorithm); + Assert.Equal(checksumSHA1, fileText.GetChecksum()); + + solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha256); + + // only file loader based documents support updating checksum alg: + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentAId).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentBId).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentCId).GetTextSynchronously(default).ChecksumAlgorithm); + + fileText = solution.GetRequiredDocument(documentDId).GetTextSynchronously(default); + Assert.Equal(SourceHashAlgorithm.Sha256, fileText.ChecksumAlgorithm); + Assert.Equal(checksumSHA256, fileText.GetChecksum()); + + solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); + + // only file loader based documents support updating checksum alg: + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentAId).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentBId).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentCId).GetTextSynchronously(default).ChecksumAlgorithm); + + fileText = solution.GetRequiredDocument(documentDId).GetTextSynchronously(default); + Assert.Equal(SourceHashAlgorithm.Sha1, fileText.ChecksumAlgorithm); + Assert.Equal(checksumSHA1, fileText.GetChecksum()); + } + [Fact] public void WithProjectName() { @@ -646,7 +734,7 @@ public void WithProjectName() SolutionTestHelpers.TestProperty( solution, (s, value) => s.WithProjectName(projectId, value), - s => s.GetProject(projectId)!.Name, + s => s.GetRequiredProject(projectId).Name, projectName, defaultThrows: true); @@ -669,7 +757,7 @@ public void WithProjectFilePath() SolutionTestHelpers.TestProperty( solution, (s, value) => s.WithProjectFilePath(projectId, value), - s => s.GetProject(projectId)!.FilePath, + s => s.GetRequiredProject(projectId).FilePath, (string?)path, defaultThrows: false); @@ -733,7 +821,7 @@ public void WithProjectParseOptions() SolutionTestHelpers.TestProperty( solution, (s, value) => s.WithProjectParseOptions(projectId, value), - s => s.GetProject(projectId)!.ParseOptions!, + s => s.GetRequiredProject(projectId).ParseOptions!, (ParseOptions)options, defaultThrows: true); @@ -1175,6 +1263,174 @@ public void RemoveAnalyzerReference() Assert.Throws(() => solution.RemoveAnalyzerReference(new TestAnalyzerReference())); } + [Fact] + public void AddDocument_Loader() + { + var projectId = ProjectId.CreateNewId(); + using var workspace = CreateWorkspace(); + var solution = workspace.CurrentSolution.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithms.Default). + WithProjectParseOptions(projectId, new CSharpParseOptions(kind: SourceCodeKind.Script)); + + var loader = new TestTextLoader(checksumAlgorithm: SourceHashAlgorithm.Sha1); + var documentId = DocumentId.CreateNewId(projectId); + var folders = new[] { "folder1", "folder2" }; + + var solution2 = solution.AddDocument(documentId, "name", loader, folders); + var document = solution2.GetRequiredDocument(documentId); + AssertEx.Equal(folders, document.Folders); + Assert.Equal(SourceCodeKind.Script, document.SourceCodeKind); + Assert.Equal(SourceHashAlgorithm.Sha1, document.GetTextSynchronously(default).ChecksumAlgorithm); + + Assert.Throws("documentId", () => solution.AddDocument(documentId: null!, "name", loader)); + Assert.Throws("name", () => solution.AddDocument(documentId, name: null!, loader)); + Assert.Throws("loader", () => solution.AddDocument(documentId, "name", loader: null!)); + Assert.Throws(() => solution.AddDocument(documentId: DocumentId.CreateNewId(ProjectId.CreateNewId()), "name", loader)); + } + + [Fact] + public void AddDocument_Text() + { + var projectId = ProjectId.CreateNewId(); + using var workspace = CreateWorkspace(); + + var solution = workspace.CurrentSolution.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithms.Default). + WithProjectParseOptions(projectId, new CSharpParseOptions(kind: SourceCodeKind.Script)); + + var documentId = DocumentId.CreateNewId(projectId); + var filePath = Path.Combine(TempRoot.Root, "x.cs"); + var folders = new[] { "folder1", "folder2" }; + + var solution2 = solution.AddDocument(documentId, "name", "text", folders, filePath); + var document = solution2.GetRequiredDocument(documentId); + var sourceText = document.GetTextSynchronously(default); + + Assert.Equal("text", sourceText.ToString()); + Assert.Equal(SourceHashAlgorithms.Default, sourceText.ChecksumAlgorithm); + AssertEx.Equal(folders, document.Folders); + Assert.Equal(filePath, document.FilePath); + Assert.False(document.State.Attributes.IsGenerated); + Assert.Equal(SourceCodeKind.Script, document.SourceCodeKind); + + Assert.Throws("documentId", () => solution.AddDocument(documentId: null!, "name", "text")); + Assert.Throws("name", () => solution.AddDocument(documentId, name: null!, "text")); + Assert.Throws("text", () => solution.AddDocument(documentId, "name", text: (string)null!)); + Assert.Throws(() => solution.AddDocument(documentId: DocumentId.CreateNewId(ProjectId.CreateNewId()), "name", "text")); + } + + [Fact] + public void AddDocument_SourceText() + { + var projectId = ProjectId.CreateNewId(); + using var workspace = CreateWorkspace(); + var solution = workspace.CurrentSolution.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithms.Default). + WithProjectParseOptions(projectId, new CSharpParseOptions(kind: SourceCodeKind.Script)); + + var documentId = DocumentId.CreateNewId(projectId); + var sourceText = SourceText.From("text", checksumAlgorithm: SourceHashAlgorithms.Default); + var filePath = Path.Combine(TempRoot.Root, "x.cs"); + var folders = new[] { "folder1", "folder2" }; + + var solution2 = solution.AddDocument(documentId, "name", sourceText, folders, filePath, isGenerated: true); + var document = solution2.GetRequiredDocument(documentId); + + AssertEx.Equal(folders, document.Folders); + Assert.Equal(filePath, document.FilePath); + Assert.True(document.State.Attributes.IsGenerated); + Assert.Equal(SourceCodeKind.Script, document.SourceCodeKind); + + Assert.Throws("documentId", () => solution.AddDocument(documentId: null!, "name", sourceText)); + Assert.Throws("name", () => solution.AddDocument(documentId, name: null!, sourceText)); + Assert.Throws("text", () => solution.AddDocument(documentId, "name", text: (SourceText)null!)); + Assert.Throws(() => solution.AddDocument(documentId: DocumentId.CreateNewId(ProjectId.CreateNewId()), "name", sourceText)); + } + + [Fact] + public void AddDocument_SyntaxRoot() + { + var projectId = ProjectId.CreateNewId(); + using var workspace = CreateWorkspace(); + + var solution = workspace.CurrentSolution.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithms.Default). + WithProjectParseOptions(projectId, new CSharpParseOptions(kind: SourceCodeKind.Script)); + + var documentId = DocumentId.CreateNewId(projectId); + var filePath = Path.Combine(TempRoot.Root, "x.cs"); + var folders = new[] { "folder1", "folder2" }; + + var root = CSharp.SyntaxFactory.ParseCompilationUnit("class C {}"); + var solution2 = solution.AddDocument(documentId, "name", root, folders, filePath); + var document2 = solution2.GetRequiredDocument(documentId); + + AssertEx.Equal(folders, document2.Folders); + Assert.Equal(filePath, document2.FilePath); + Assert.False(document2.State.Attributes.IsGenerated); + Assert.Equal(SourceCodeKind.Script, document2.SourceCodeKind); + + Assert.Throws("documentId", () => solution.AddDocument(documentId: null!, "name", root)); + Assert.Throws("name", () => solution.AddDocument(documentId, name: null!, root)); + Assert.Throws("syntaxRoot", () => solution.AddDocument(documentId, "name", syntaxRoot: null!)); + Assert.Throws(() => solution.AddDocument(documentId: DocumentId.CreateNewId(ProjectId.CreateNewId()), "name", syntaxRoot: root)); + } + + [Fact] + public void AddDocument_SyntaxRoot_ExplicitTree() + { + var projectId = ProjectId.CreateNewId(); + using var workspace = CreateWorkspace(); + + var solution = workspace.CurrentSolution.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithms.Default). + WithProjectParseOptions(projectId, new CSharpParseOptions(kind: SourceCodeKind.Script)); + + var documentId = DocumentId.CreateNewId(projectId); + var filePath = Path.Combine(TempRoot.Root, "x.cs"); + var folders = new[] { "folder1", "folder2" }; + + var root = CSharp.SyntaxFactory.ParseSyntaxTree(SourceText.From("class C {}", encoding: null, SourceHashAlgorithm.Sha1)).GetRoot(); + Assert.Equal(SourceHashAlgorithm.Sha1, root.SyntaxTree.GetText().ChecksumAlgorithm); + + var solution2 = solution.AddDocument(documentId, "name", root, folders, filePath); + var document2 = solution2.GetRequiredDocument(documentId); + var sourceText = document2.GetTextSynchronously(default); + Assert.Equal("class C {}", sourceText.ToString()); + + // the checksum algorithm of the tree is ignored, instead the one set on the project is used: + Assert.Equal(SourceHashAlgorithms.Default, sourceText.ChecksumAlgorithm); + + AssertEx.Equal(folders, document2.Folders); + Assert.Equal(filePath, document2.FilePath); + Assert.False(document2.State.Attributes.IsGenerated); + Assert.Equal(SourceCodeKind.Script, document2.SourceCodeKind); + } + + [Fact] + public void AddDocument_SyntaxRoot_SynthesizedTree() + { + var projectId = ProjectId.CreateNewId(); + using var workspace = CreateWorkspace(); + + var solution = workspace.CurrentSolution.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithms.Default). + WithProjectParseOptions(projectId, new CSharpParseOptions(kind: SourceCodeKind.Script)); + + var documentId = DocumentId.CreateNewId(projectId); + + var root = CSharp.SyntaxFactory.ParseCompilationUnit("class C {}"); + Assert.Equal(SourceHashAlgorithm.Sha1, root.SyntaxTree.GetText().ChecksumAlgorithm); + + var solution2 = solution.AddDocument(documentId, "name", root); + var document2 = solution2.GetRequiredDocument(documentId); + var sourceText = document2.GetTextSynchronously(default); + Assert.Equal("class C {}", sourceText.ToString()); + + // the checksum algorithm of the tree is ignored, instead the one set on the project is used: + Assert.Equal(SourceHashAlgorithms.Default, sourceText.ChecksumAlgorithm); + } + #nullable disable [Fact] public void TestAddProject() @@ -1861,6 +2117,49 @@ public void TestUpdateSyntaxTreeWithAnnotations() Assert.True(root2.HasAnnotation(annotation)); } + [Theory] + [InlineData(LanguageNames.CSharp)] + [InlineData(LanguageNames.VisualBasic)] + public async Task ParsedTreeRootOwnership(string language) + { + var pid = ProjectId.CreateNewId(); + var did = DocumentId.CreateNewId(pid); + + var source = (language == LanguageNames.CSharp) ? "class C {}" : "Class C : End Class"; + + using var workspace = CreateWorkspace(); + + var sol = workspace.CurrentSolution + .AddProject(pid, "test", "test.dll", language) + .AddDocument(did, "test", source); + + var document = sol.GetDocument(did); + + // update the document syntax root: + var syntaxRoot = await document.GetSyntaxRootAsync(CancellationToken.None); + + SyntaxNode newSyntaxRoot; + if (language == LanguageNames.CSharp) + { + var classNode = syntaxRoot.DescendantNodes().OfType().Single(); + newSyntaxRoot = syntaxRoot.ReplaceNode(classNode, classNode.WithModifiers(CS.SyntaxFactory.TokenList(CS.SyntaxFactory.ParseToken("public")))); + } + else + { + var classNode = syntaxRoot.DescendantNodes().OfType().Single(); + newSyntaxRoot = syntaxRoot.ReplaceNode(classNode, classNode.WithModifiers(VB.SyntaxFactory.TokenList(VB.SyntaxFactory.ParseToken("Public")))); + } + + var documentWithAttribute = document.WithSyntaxRoot(newSyntaxRoot); + + var tree = await documentWithAttribute.GetSyntaxTreeAsync(CancellationToken.None); + var root = await documentWithAttribute.GetRequiredSyntaxRootAsync(CancellationToken.None); + + Assert.Same(tree, root.SyntaxTree); + Assert.Same(tree, tree.WithRootAndOptions(root, tree.Options)); + Assert.Same(tree, tree.WithFilePath(tree.FilePath)); + } + [Fact] public void TestUpdatingFilePathUpdatesSyntaxTree() { @@ -1926,7 +2225,7 @@ public void TestDocumentChangedOnDiskIsNotObserved() var did = DocumentId.CreateNewId(pid); sol = sol.AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8, SourceHashAlgorithms.Default)); var observedText = GetObservedText(sol, did, text1); @@ -1985,7 +2284,7 @@ public void TestGetLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8, SourceHashAlgorithms.Default)); var doc = sol.GetDocument(did); @@ -2052,7 +2351,7 @@ public void TestGetSyntaxTreeFromLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8, SourceHashAlgorithms.Default)); var doc = sol.GetDocument(did); var docTree = doc.GetSyntaxTreeAsync().Result; @@ -2571,7 +2870,7 @@ public async Task TestDocumentFileAccessFailureMissingFile() var did = DocumentId.CreateNewId(pid); solution = solution.AddProject(pid, "goo", "goo", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(solution.Services, @"C:\doesnotexist.cs", Encoding.UTF8)) + .AddDocument(did, "x", new WorkspaceFileTextLoader(solution.Services, @"C:\doesnotexist.cs", Encoding.UTF8, SourceHashAlgorithms.Default)) .WithDocumentFilePath(did, "document path"); var doc = solution.GetDocument(did); @@ -2833,11 +3132,11 @@ public void TestProjectCompletenessWithMultipleProjects() Assert.True(transitivelyDependsOnNormalProjects.HasSuccessfullyLoadedAsync().Result); } - private class TestSmallFileTextLoader : FileTextLoader + private sealed class TestSmallFileTextLoader : FileTextLoader { public TestSmallFileTextLoader(string path, Encoding encoding) #pragma warning disable RS0030 // Do not used banned APIs - : base(path, encoding) + : base(path, encoding, SourceHashAlgorithms.Default) #pragma warning restore { } @@ -2898,20 +3197,23 @@ public void TestWithSyntaxTree() var factory = dummyProject.Services.GetService(); // create the origin tree - var strongTree = factory.ParseSyntaxTree("dummy", dummyProject.ParseOptions, SourceText.From("// emtpy"), CancellationToken.None); + var strongTree = factory.ParseSyntaxTree("dummy", dummyProject.ParseOptions, SourceText.From("// empty", encoding: null, SourceHashAlgorithms.Default), CancellationToken.None); // create recoverable tree off the original tree + var sourceText = strongTree.GetText(); var recoverableTree = factory.CreateRecoverableTree( dummyProject.Id, strongTree.FilePath, strongTree.Options, - new ConstantValueSource(TextAndVersion.Create(strongTree.GetText(), VersionStamp.Create(), strongTree.FilePath)), - strongTree.GetText().Encoding, + new ConstantTextAndVersionSource(TextAndVersion.Create(sourceText, VersionStamp.Create(), strongTree.FilePath)), + sourceText.Encoding, strongTree.GetRoot()); // create new tree before it ever getting root node var newTree = recoverableTree.WithFilePath("different/dummy"); + Assert.Equal(SourceHashAlgorithms.Default, recoverableTree.GetText().ChecksumAlgorithm); + // this shouldn't throw _ = newTree.GetRoot(); } diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index f4a59254b13e9..4d575f258bd79 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -770,13 +770,8 @@ public async Task DynamicFilesNotPassedToSourceGenerators() var documentInfo = DocumentInfo.Create( DocumentId.CreateNewId(project.Id), name: "Test.cs", - folders: new string[] { }, - sourceCodeKind: SourceCodeKind.Regular, - loader: null, - filePath: null, - isGenerated: true, - designTimeOnly: true, - documentServiceProvider: null); + isGenerated: true).WithDesignTimeOnly(true); + project = project.Solution.AddDocument(documentInfo).Projects.Single() .AddAnalyzerReference(analyzerReference); diff --git a/src/Workspaces/CoreTest/UtilityTest/SourceTextSerializationTests.cs b/src/Workspaces/CoreTest/UtilityTest/SourceTextSerializationTests.cs index 3f036a0f32c64..d6bb8d2708632 100644 --- a/src/Workspaces/CoreTest/UtilityTest/SourceTextSerializationTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/SourceTextSerializationTests.cs @@ -41,7 +41,7 @@ public void TestSourceTextSerialization() stream.Position = 0; using var reader = ObjectReader.TryGetReader(stream); - var recovered = SourceTextExtensions.ReadFrom(textService, reader, originalText.Encoding, CancellationToken.None); + var recovered = SourceTextExtensions.ReadFrom(textService, reader, originalText.Encoding, originalText.ChecksumAlgorithm, CancellationToken.None); Assert.Equal(originalText.ToString(), recovered.ToString()); } diff --git a/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceTests.cs b/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceTests.cs index dd6994316514a..d8cdf07c850ab 100644 --- a/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceTests.cs +++ b/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceTests.cs @@ -186,7 +186,7 @@ public Document AddDocument(DocumentInfo documentInfo) } } - [Fact, Obsolete] + [Fact, Obsolete("Testing obsolete API")] public void SetOptions_PublicGlobalOptions() { using var workspace1 = new AdhocWorkspace(); diff --git a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs index 9845f3a4f75c3..1f75b2756aabe 100644 --- a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs +++ b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs @@ -15,10 +15,15 @@ internal class TestTextLoader : TextLoader { private readonly string _text; - public TestTextLoader(string text = "test") - => _text = text; + internal override SourceHashAlgorithm ChecksumAlgorithm { get; } + + public TestTextLoader(string text = "test", SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default) + { + _text = text; + ChecksumAlgorithm = checksumAlgorithm; + } public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create())); + => Task.FromResult(TextAndVersion.Create(SourceText.From(_text, encoding: null, ChecksumAlgorithm), VersionStamp.Create())); } } diff --git a/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj index 1bb40b6b4f396..67d850f7b397f 100644 --- a/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj +++ b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj @@ -12,7 +12,8 @@ Properties CSharpProject CSharpProject - v4.8 + v4.8 + SHA1 512 snKey.snk diff --git a/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs b/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs index 3e0f0819c4b85..96aed348776b8 100644 --- a/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs +++ b/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.Linq; using Microsoft.Build.Locator; using Roslyn.Test.Utilities; @@ -17,16 +18,20 @@ internal class VisualStudioMSBuildInstalled : ExecutionCondition static VisualStudioMSBuildInstalled() { - var installedVisualStudios = MSBuildLocator.QueryVisualStudioInstances().ToArray(); - foreach (var visualStudioInstall in installedVisualStudios) + var latestInstalledInstance = (VisualStudioInstance?)null; + foreach (var visualStudioInstance in MSBuildLocator.QueryVisualStudioInstances()) { - if (visualStudioInstall.Version.Major == 17 && - visualStudioInstall.Version.Minor == 0) + if (latestInstalledInstance == null || visualStudioInstance.Version > latestInstalledInstance.Version) { - MSBuildLocator.RegisterInstance(visualStudioInstall); - s_instance = visualStudioInstall; + latestInstalledInstance = visualStudioInstance; } } + + if (latestInstalledInstance != null) + { + MSBuildLocator.RegisterInstance(latestInstalledInstance); + s_instance = latestInstalledInstance; + } } #endif diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 610f6b4b3d879..20c4bd27b7041 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -247,6 +247,40 @@ public async Task TestCompilationOutputInfo() Assert.Equal("VisualBasicProject.dll", Path.GetFileName(p2.CompilationOutputInfo.AssemblyPath)); } + [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] + public async Task TestChecksumAlgorithm() + { + CreateFiles(GetSimpleCSharpSolutionWithAdditionaFile()); + var solutionFilePath = GetSolutionFileName("TestSolution.sln"); + + using var workspace = CreateMSBuildWorkspace(); + var solution = await workspace.OpenSolutionAsync(solutionFilePath); + var project = solution.Projects.Single(); + + Assert.Equal(SourceHashAlgorithm.Sha1, project.State.ChecksumAlgorithm); + + Assert.All(project.Documents, d => Assert.Equal(SourceHashAlgorithm.Sha1, d.GetTextSynchronously(default).ChecksumAlgorithm)); + Assert.All(project.AdditionalDocuments, d => Assert.Equal(SourceHashAlgorithm.Sha1, d.GetTextSynchronously(default).ChecksumAlgorithm)); + } + + [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] + public async Task TestChecksumAlgorithm_Default() + { + CreateFiles(GetMultiProjectSolutionFiles()); + var solutionFilePath = GetSolutionFileName("TestSolution.sln"); + + using var workspace = CreateMSBuildWorkspace(); + var sol = await workspace.OpenSolutionAsync(solutionFilePath); + var csProject = sol.Projects.First(p => p.Language == LanguageNames.CSharp); + var vbProject = sol.Projects.First(p => p.Language == LanguageNames.VisualBasic); + + Assert.Equal(SourceHashAlgorithms.Default, csProject.State.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, vbProject.State.ChecksumAlgorithm); + + Assert.All(csProject.Documents, d => Assert.Equal(SourceHashAlgorithms.Default, d.GetTextSynchronously(default).ChecksumAlgorithm)); + Assert.All(vbProject.Documents, d => Assert.Equal(SourceHashAlgorithms.Default, d.GetTextSynchronously(default).ChecksumAlgorithm)); + } + [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] public async Task TestCrossLanguageReferencesUsesInMemoryGeneratedMetadata() { diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 2bc3e967a9a3b..4c2fb6aac5256 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -48,15 +48,15 @@ public async Task CreateProjectInfoAsync(Checksum projectChecksum, { var projectChecksums = await GetAssetAsync(projectChecksum, cancellationToken).ConfigureAwait(false); - var projectInfo = await GetAssetAsync(projectChecksums.Info, cancellationToken).ConfigureAwait(false); - if (!RemoteSupportedLanguages.IsSupported(projectInfo.Language)) + var attributes = await GetAssetAsync(projectChecksums.Info, cancellationToken).ConfigureAwait(false); + if (!RemoteSupportedLanguages.IsSupported(attributes.Language)) { // only add project our workspace supports. // workspace doesn't allow creating project with unknown languages return null; } - var compilationOptions = projectInfo.FixUpCompilationOptions( + var compilationOptions = attributes.FixUpCompilationOptions( await GetAssetAsync(projectChecksums.CompilationOptions, cancellationToken).ConfigureAwait(false)); var parseOptions = await GetAssetAsync(projectChecksums.ParseOptions, cancellationToken).ConfigureAwait(false); @@ -70,13 +70,7 @@ public async Task CreateProjectInfoAsync(Checksum projectChecksum, var analyzerConfigDocumentInfos = await CreateDocumentInfosAsync(projectChecksums.AnalyzerConfigDocuments, cancellationToken).ConfigureAwait(false); return ProjectInfo.Create( - projectInfo.Id, - projectInfo.Version, - projectInfo.Name, - projectInfo.AssemblyName, - projectInfo.Language, - projectInfo.FilePath, - projectInfo.OutputFilePath, + attributes, compilationOptions, parseOptions, documentInfos, @@ -84,39 +78,24 @@ public async Task CreateProjectInfoAsync(Checksum projectChecksum, metadataReferences, analyzerReferences, additionalDocumentInfos, - projectInfo.IsSubmission) - .WithOutputRefFilePath(projectInfo.OutputRefFilePath) - .WithCompilationOutputInfo(projectInfo.CompilationOutputInfo) - .WithHasAllInformation(projectInfo.HasAllInformation) - .WithRunAnalyzers(projectInfo.RunAnalyzers) - .WithDefaultNamespace(projectInfo.DefaultNamespace) - .WithAnalyzerConfigDocuments(analyzerConfigDocumentInfos) - .WithTelemetryId(projectInfo.TelemetryId); + analyzerConfigDocumentInfos, + hostObjectType: null); // TODO: https://github.com/dotnet/roslyn/issues/62804 } public async Task CreateDocumentInfoAsync(Checksum documentChecksum, CancellationToken cancellationToken) { var documentSnapshot = await GetAssetAsync(documentChecksum, cancellationToken).ConfigureAwait(false); - var documentInfo = await GetAssetAsync(documentSnapshot.Info, cancellationToken).ConfigureAwait(false); + var attributes = await GetAssetAsync(documentSnapshot.Info, cancellationToken).ConfigureAwait(false); var serializableSourceText = await GetAssetAsync(documentSnapshot.Text, cancellationToken).ConfigureAwait(false); var textLoader = TextLoader.From( TextAndVersion.Create( await serializableSourceText.GetTextAsync(cancellationToken).ConfigureAwait(false), VersionStamp.Create(), - documentInfo.FilePath)); + attributes.FilePath)); // TODO: do we need version? - return DocumentInfo.Create( - documentInfo.Id, - documentInfo.Name, - documentInfo.Folders, - documentInfo.SourceCodeKind, - textLoader, - documentInfo.FilePath, - documentInfo.IsGenerated, - documentInfo.DesignTimeOnly, - documentServiceProvider: null); + return new DocumentInfo(attributes, textLoader, documentServiceProvider: null); } private async Task> CreateDocumentInfosAsync(ChecksumCollection documentChecksums, CancellationToken cancellationToken) diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index 39eb1d047e6a7..abab7a2f5aa83 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -300,54 +300,7 @@ private async Task UpdateProjectInfoAsync(Project project, Checksum inf Contract.ThrowIfFalse(project.State.ProjectInfo.Attributes.Language == newProjectAttributes.Language); Contract.ThrowIfFalse(project.State.ProjectInfo.Attributes.IsSubmission == newProjectAttributes.IsSubmission); - var projectId = project.Id; - - if (project.State.ProjectInfo.Attributes.Name != newProjectAttributes.Name) - { - project = project.Solution.WithProjectName(projectId, newProjectAttributes.Name).GetProject(projectId)!; - } - - if (project.State.ProjectInfo.Attributes.AssemblyName != newProjectAttributes.AssemblyName) - { - project = project.Solution.WithProjectAssemblyName(projectId, newProjectAttributes.AssemblyName).GetProject(projectId)!; - } - - if (project.State.ProjectInfo.Attributes.FilePath != newProjectAttributes.FilePath) - { - project = project.Solution.WithProjectFilePath(projectId, newProjectAttributes.FilePath).GetProject(projectId)!; - } - - if (project.State.ProjectInfo.Attributes.OutputFilePath != newProjectAttributes.OutputFilePath) - { - project = project.Solution.WithProjectOutputFilePath(projectId, newProjectAttributes.OutputFilePath).GetProject(projectId)!; - } - - if (project.State.ProjectInfo.Attributes.OutputRefFilePath != newProjectAttributes.OutputRefFilePath) - { - project = project.Solution.WithProjectOutputRefFilePath(projectId, newProjectAttributes.OutputRefFilePath).GetProject(projectId)!; - } - - if (project.State.ProjectInfo.Attributes.CompilationOutputInfo != newProjectAttributes.CompilationOutputInfo) - { - project = project.Solution.WithProjectCompilationOutputInfo(project.Id, newProjectAttributes.CompilationOutputInfo).GetProject(project.Id)!; - } - - if (project.State.ProjectInfo.Attributes.DefaultNamespace != newProjectAttributes.DefaultNamespace) - { - project = project.Solution.WithProjectDefaultNamespace(projectId, newProjectAttributes.DefaultNamespace).GetProject(projectId)!; - } - - if (project.State.ProjectInfo.Attributes.HasAllInformation != newProjectAttributes.HasAllInformation) - { - project = project.Solution.WithHasAllInformation(projectId, newProjectAttributes.HasAllInformation).GetProject(projectId)!; - } - - if (project.State.ProjectInfo.Attributes.RunAnalyzers != newProjectAttributes.RunAnalyzers) - { - project = project.Solution.WithRunAnalyzers(projectId, newProjectAttributes.RunAnalyzers).GetProject(projectId)!; - } - - return project; + return project.Solution.WithProjectAttributes(project.Id, newProjectAttributes).GetRequiredProject(project.Id); } private async Task UpdateDocumentsAsync( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs index beb42443acbcf..f3d59fa21e145 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs @@ -117,6 +117,8 @@ public AsyncLazy(Func> asynchronousComputeFunction, F _cacheResult = cacheResult; } + public bool CacheResult => _cacheResult; + #region Lock Wrapper for Invariant Checking /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ValuesSources/ConstantValueSource.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ValuesSources/ConstantValueSource.cs index 7e7950aa10813..cef111912277d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ValuesSources/ConstantValueSource.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ValuesSources/ConstantValueSource.cs @@ -11,20 +11,21 @@ namespace Roslyn.Utilities /// /// This value source keeps a strong reference to a value. /// - internal sealed class ConstantValueSource : ValueSource + internal class ConstantValueSource : ValueSource { - private readonly T _value; + public T Value { get; } + private Task? _task; public ConstantValueSource(T value) - => _value = value; + => Value = value; public override T GetValue(CancellationToken cancellationToken = default) - => _value; + => Value; public override bool TryGetValue([MaybeNullWhen(false)] out T value) { - value = _value; + value = Value; return true; } @@ -32,7 +33,7 @@ public override Task GetValueAsync(CancellationToken cancellationToken = defa { if (_task == null) { - Interlocked.CompareExchange(ref _task, Task.FromResult(_value), null); + Interlocked.CompareExchange(ref _task, Task.FromResult(Value), null); } return _task; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs index 2cb130f54a267..7667891ab6e08 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs @@ -102,20 +102,6 @@ public static HostLanguageServices GetExtendedLanguageServices(this Project proj return project.AnalyzerConfigDocuments.FirstOrDefault(d => d.FilePath == analyzerConfigPath); } - public static AnalyzerConfigDocument? GetOrCreateAnalyzerConfigDocument(this Project project, string analyzerConfigPath) - { - var existingAnalyzerConfigDocument = project.TryGetExistingAnalyzerConfigDocumentAtPath(analyzerConfigPath); - if (existingAnalyzerConfigDocument != null) - { - return existingAnalyzerConfigDocument; - } - - var id = DocumentId.CreateNewId(project.Id); - var documentInfo = DocumentInfo.Create(id, ".editorconfig", filePath: analyzerConfigPath); - var newSolution = project.Solution.AddAnalyzerConfigDocuments(ImmutableArray.Create(documentInfo)); - return newSolution.GetProject(project.Id)?.GetAnalyzerConfigDocument(id); - } - public static async Task GetRequiredCompilationAsync(this Project project, CancellationToken cancellationToken) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj index bf9b842f6423e..10eb8fe1382e3 100644 --- a/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj +++ b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj @@ -53,9 +53,6 @@ - - - diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.NodeSyntaxReference.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.NodeSyntaxReference.vb new file mode 100644 index 0000000000000..6c49abea552d9 --- /dev/null +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.NodeSyntaxReference.vb @@ -0,0 +1,38 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System +Imports System.Threading +Imports Microsoft.CodeAnalysis.Text + +Namespace Microsoft.CodeAnalysis.VisualBasic + Partial Friend Class VisualBasicSyntaxTreeFactoryServiceFactory + Partial Friend Class VisualBasicSyntaxTreeFactoryService + Private NotInheritable Class NodeSyntaxReference + Inherits SyntaxReference + Private ReadOnly _node As SyntaxNode + + Friend Sub New(node As SyntaxNode) + _node = node + End Sub + + Public Overrides ReadOnly Property SyntaxTree As SyntaxTree + Get + Return _node.SyntaxTree + End Get + End Property + + Public Overrides ReadOnly Property Span As TextSpan + Get + Return _node.Span + End Get + End Property + + Public Overrides Function GetSyntax(Optional cancellationToken As CancellationToken = Nothing) As SyntaxNode + Return _node + End Function + End Class + End Class + End Class +End Namespace diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb new file mode 100644 index 0000000000000..878418ef03514 --- /dev/null +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb @@ -0,0 +1,90 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Runtime.InteropServices +Imports System.Text +Imports System.Threading +Imports Microsoft.CodeAnalysis.Text + +Namespace Microsoft.CodeAnalysis.VisualBasic + Partial Friend Class VisualBasicSyntaxTreeFactoryServiceFactory + Partial Friend Class VisualBasicSyntaxTreeFactoryService + ''' + ''' Parsed that creates with given encoding And checksum algorithm. + ''' + Partial Private NotInheritable Class ParsedSyntaxTree + Inherits VisualBasicSyntaxTree + + Private ReadOnly _root As VisualBasicSyntaxNode + Private ReadOnly _checksumAlgorithm As SourceHashAlgorithm + + Public Overrides ReadOnly Property Encoding As Encoding + Public Overrides ReadOnly Property Options As VisualBasicParseOptions + Public Overrides ReadOnly Property FilePath As String + + Private _lazyText As SourceText + + Public Sub New(lazyText As SourceText, root As VisualBasicSyntaxNode, options As VisualBasicParseOptions, filePath As String, encoding As Encoding, checksumAlgorithm As SourceHashAlgorithm) + _lazyText = lazyText + _root = CloneNodeAsRoot(root) + _checksumAlgorithm = checksumAlgorithm + + Me.Encoding = encoding + Me.Options = options + Me.FilePath = filePath + End Sub + + Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText + If _lazyText Is Nothing Then + Interlocked.CompareExchange(_lazyText, GetRoot(cancellationToken).GetText(Encoding, _checksumAlgorithm), Nothing) + End If + + Return _lazyText + End Function + + Public Overrides Function TryGetText( ByRef text As SourceText) As Boolean + text = _lazyText + Return text IsNot Nothing + End Function + + Public Overrides ReadOnly Property Length As Integer + Get + Return _root.FullSpan.Length + End Get + End Property + + Public Overrides ReadOnly Property HasCompilationUnitRoot As Boolean + Get + Return True + End Get + End Property + + Public Overrides Function GetRoot(Optional cancellationToken As CancellationToken = Nothing) As VisualBasicSyntaxNode + Return _root + End Function + + Public Overrides Function TryGetRoot( ByRef root As VisualBasicSyntaxNode) As Boolean + root = _root + Return True + End Function + + Public Overrides Function WithRootAndOptions(root As SyntaxNode, options As ParseOptions) As SyntaxTree + Return If(ReferenceEquals(root, _root) AndAlso options = Me.Options, + Me, + New ParsedSyntaxTree(lazyText:=Nothing, DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), FilePath, Encoding, _checksumAlgorithm)) + End Function + + Public Overrides Function WithFilePath(path As String) As SyntaxTree + Return If(path = FilePath, + Me, + New ParsedSyntaxTree(_lazyText, _root, Options, path, Encoding, _checksumAlgorithm)) + End Function + + Public Overrides Function GetReference(node As SyntaxNode) As SyntaxReference + Return New NodeSyntaxReference(node) + End Function + End Class + End Class + End Class +End Namespace diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb index 9317fb08c0386..c0814d64644f3 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb @@ -52,7 +52,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic cacheKey As ProjectId, filePath As String, options As ParseOptions, - text As ValueSource(Of TextAndVersion), + text As ITextAndVersionSource, encoding As Encoding, root As VisualBasicSyntaxNode) As SyntaxTree Return New RecoverableSyntaxTree( diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb index 661d8c0775168..08c66404dcc3b 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb @@ -84,12 +84,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return SyntaxFactory.ParseSyntaxTree(text, options, filePath, cancellationToken) End Function - Public Overrides Function CreateSyntaxTree(filePath As String, options As ParseOptions, encoding As Encoding, root As SyntaxNode) As SyntaxTree + Public Overrides Function CreateSyntaxTree(filePath As String, options As ParseOptions, encoding As Encoding, checksumAlgorithm As SourceHashAlgorithm, root As SyntaxNode) As SyntaxTree If options Is Nothing Then options = GetDefaultParseOptions() End If - Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), filePath, encoding) + Return New ParsedSyntaxTree(lazyText:=Nothing, DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), filePath, encoding, checksumAlgorithm) End Function Public Overrides Function CanCreateRecoverableTree(root As SyntaxNode) As Boolean @@ -100,7 +100,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides Function CreateRecoverableTree(cacheKey As ProjectId, filePath As String, optionsOpt As ParseOptions, - text As ValueSource(Of TextAndVersion), + text As ITextAndVersionSource, encoding As Encoding, root As SyntaxNode) As SyntaxTree From 07783152ae4846500cea609c34935d944f966f44 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 19 Sep 2022 11:26:47 -0700 Subject: [PATCH 04/32] Feedback --- .../CSharpSyntaxTree.DebuggerSyntaxTree.cs | 5 +- .../Test/Utilities/CSharp/CSharpTestSource.cs | 4 +- .../Utilities/VisualBasic/BasicTestSource.vb | 2 + ...isualBasicSyntaxTree.DebuggerSyntaxTree.vb | 3 ++ .../Workspace/Solution/ProjectState.cs | 54 +++++++++++++------ .../Portable/Workspace/Solution/Solution.cs | 4 +- .../Workspace/Solution/SolutionState.cs | 4 +- .../VisualStudioMSBuildWorkspaceTests.cs | 15 +++--- .../Host/RemoteWorkspace.SolutionCreator.cs | 3 +- 9 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.DebuggerSyntaxTree.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.DebuggerSyntaxTree.cs index 2f8b10e52bc5e..5a62d234aa311 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.DebuggerSyntaxTree.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.DebuggerSyntaxTree.cs @@ -8,7 +8,10 @@ namespace Microsoft.CodeAnalysis.CSharp { public partial class CSharpSyntaxTree { - private class DebuggerSyntaxTree : ParsedSyntaxTree + /// + /// Use by Expression Evaluator. + /// + private sealed class DebuggerSyntaxTree : ParsedSyntaxTree { public DebuggerSyntaxTree(CSharpSyntaxNode root, SourceText text, CSharpParseOptions options) : base( diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs index 0a910109b1e6f..0383c01759ca8 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs @@ -49,7 +49,9 @@ private static void CheckSerializable(SyntaxTree tree) var root = tree.GetRoot(); root.SerializeTo(stream); stream.Position = 0; - CSharpSyntaxNode.DeserializeFrom(stream); + + // verify absence of exception: + _ = CSharpSyntaxNode.DeserializeFrom(stream); } public SyntaxTree[] GetSyntaxTrees(CSharpParseOptions parseOptions, string sourceFileName = "") diff --git a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb index 0f0381e96e4f6..a6204f6cb6104 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb @@ -32,6 +32,8 @@ Public Structure BasicTestSource Dim root = tree.GetRoot() root.SerializeTo(stream) stream.Position = 0 + + ' verify absence of exception VisualBasicSyntaxNode.DeserializeFrom(stream) End Using End Sub diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb index 6f1086a0201b7..961b382d97b6e 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DebuggerSyntaxTree.vb @@ -7,6 +7,9 @@ Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic Partial Public MustInherit Class VisualBasicSyntaxTree + ''' + ''' Use by Expression Evaluator. + ''' Private NotInheritable Class DebuggerSyntaxTree Inherits ParsedSyntaxTree diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index cb81f81f44ac5..82b5029d78cdb 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -694,38 +694,55 @@ private ProjectState With( internal ProjectInfo.ProjectAttributes Attributes => ProjectInfo.Attributes; - private ProjectState WithRawAttributes(ProjectInfo.ProjectAttributes attributes) - => With(projectInfo: ProjectInfo.With(attributes: attributes)); + /// + /// Updates to a newer version of attributes. + /// + private ProjectState WithNewerAttributes(ProjectInfo.ProjectAttributes attributes) + { + // version must have already been updated: + Debug.Assert(attributes.Version != Attributes.Version); - public ProjectState WithAttributes(ProjectInfo.ProjectAttributes attributes) - => (attributes == Attributes) ? this : WithRawAttributes(attributes.With(version: Version.GetNewerVersion())); + return With(projectInfo: ProjectInfo.With(attributes: attributes)); + } + + public ProjectState WithAttributes(ProjectInfo.ProjectAttributes attributes, bool updateDocuments) + { + if (attributes == Attributes) + { + return this; + } + + return With( + projectInfo: ProjectInfo.With(attributes: attributes.With(version: Version.GetNewerVersion())), + documentStates: updateDocuments ? UpdateDocumentsChecksumAlgorithm(attributes.ChecksumAlgorithm) : null); + } public ProjectState WithName(string name) - => (name == Name) ? this : WithRawAttributes(Attributes.With(name: name, version: Version.GetNewerVersion())); + => (name == Name) ? this : WithNewerAttributes(Attributes.With(name: name, version: Version.GetNewerVersion())); public ProjectState WithFilePath(string? filePath) - => (filePath == FilePath) ? this : WithRawAttributes(Attributes.With(filePath: filePath, version: Version.GetNewerVersion())); + => (filePath == FilePath) ? this : WithNewerAttributes(Attributes.With(filePath: filePath, version: Version.GetNewerVersion())); public ProjectState WithAssemblyName(string assemblyName) - => (assemblyName == AssemblyName) ? this : WithRawAttributes(Attributes.With(assemblyName: assemblyName, version: Version.GetNewerVersion())); + => (assemblyName == AssemblyName) ? this : WithNewerAttributes(Attributes.With(assemblyName: assemblyName, version: Version.GetNewerVersion())); public ProjectState WithOutputFilePath(string? outputFilePath) - => (outputFilePath == OutputFilePath) ? this : WithRawAttributes(Attributes.With(outputPath: outputFilePath, version: Version.GetNewerVersion())); + => (outputFilePath == OutputFilePath) ? this : WithNewerAttributes(Attributes.With(outputPath: outputFilePath, version: Version.GetNewerVersion())); public ProjectState WithOutputRefFilePath(string? outputRefFilePath) - => (outputRefFilePath == OutputRefFilePath) ? this : WithRawAttributes(Attributes.With(outputRefPath: outputRefFilePath, version: Version.GetNewerVersion())); + => (outputRefFilePath == OutputRefFilePath) ? this : WithNewerAttributes(Attributes.With(outputRefPath: outputRefFilePath, version: Version.GetNewerVersion())); public ProjectState WithCompilationOutputInfo(in CompilationOutputInfo info) - => (info == CompilationOutputInfo) ? this : WithRawAttributes(Attributes.With(compilationOutputInfo: info, version: Version.GetNewerVersion())); + => (info == CompilationOutputInfo) ? this : WithNewerAttributes(Attributes.With(compilationOutputInfo: info, version: Version.GetNewerVersion())); public ProjectState WithDefaultNamespace(string? defaultNamespace) - => (defaultNamespace == DefaultNamespace) ? this : WithRawAttributes(Attributes.With(defaultNamespace: defaultNamespace, version: Version.GetNewerVersion())); + => (defaultNamespace == DefaultNamespace) ? this : WithNewerAttributes(Attributes.With(defaultNamespace: defaultNamespace, version: Version.GetNewerVersion())); public ProjectState WithHasAllInformation(bool hasAllInformation) - => (hasAllInformation == HasAllInformation) ? this : WithRawAttributes(Attributes.With(hasAllInformation: hasAllInformation, version: Version.GetNewerVersion())); + => (hasAllInformation == HasAllInformation) ? this : WithNewerAttributes(Attributes.With(hasAllInformation: hasAllInformation, version: Version.GetNewerVersion())); public ProjectState WithRunAnalyzers(bool runAnalyzers) - => (runAnalyzers == RunAnalyzers) ? this : WithRawAttributes(Attributes.With(runAnalyzers: runAnalyzers, version: Version.GetNewerVersion())); + => (runAnalyzers == RunAnalyzers) ? this : WithNewerAttributes(Attributes.With(runAnalyzers: runAnalyzers, version: Version.GetNewerVersion())); public ProjectState WithChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) { @@ -734,12 +751,17 @@ public ProjectState WithChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) return this; } - // Update checksum algorithm of all documents backed by a text loader that supports reloading the content and calculating a new checksum (e.g. file loader). return With( - projectInfo: ProjectInfo.WithChecksumAlgorithm(checksumAlgorithm).WithVersion(Version.GetNewerVersion()), - documentStates: DocumentStates.UpdateStates(static (state, checksumAlgorithm) => state.UpdateChecksumAlgorithm(checksumAlgorithm), checksumAlgorithm)); + projectInfo: ProjectInfo.With(attributes: Attributes.With(checksumAlgorithm: checksumAlgorithm, version: Version.GetNewerVersion())), + documentStates: UpdateDocumentsChecksumAlgorithm(checksumAlgorithm)); } + /// + /// Update checksum algorithm of all documents backed by a text loader that supports reloading the content and calculating a new checksum (e.g. file loader). + /// + private TextDocumentStates UpdateDocumentsChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + => DocumentStates.UpdateStates(static (state, checksumAlgorithm) => state.UpdateChecksumAlgorithm(checksumAlgorithm), checksumAlgorithm); + public ProjectState WithCompilationOptions(CompilationOptions options) { if (options == CompilationOptions) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index 13a435745f3c0..2cbbb2ea414a1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -570,11 +570,11 @@ internal Solution WithRunAnalyzers(ProjectId projectId, bool runAnalyzers) /// Creates a new solution instance with the project specified updated to have the attributes, /// except for which is auto-incremented. /// - internal Solution WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes) + internal Solution WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes, bool updateDocuments) { CheckContainsProject(projectId); - var newState = _state.WithProjectAttributes(projectId, attributes); + var newState = _state.WithProjectAttributes(projectId, attributes, updateDocuments); if (newState == _state) { return this; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 094ba2d106332..3bd0618d5ada4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -835,10 +835,10 @@ public SolutionState WithRunAnalyzers(ProjectId projectId, bool runAnalyzers) /// Create a new solution instance with the project specified updated to have /// the specified attributes, except for which is auto-incremented. /// - public SolutionState WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes) + public SolutionState WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes, bool updateDocuments) { var oldProject = GetRequiredProjectState(projectId); - var newProject = oldProject.WithAttributes(attributes); + var newProject = oldProject.WithAttributes(attributes, updateDocuments); if (oldProject == newProject) { diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 20c4bd27b7041..8c48f585775a1 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -263,22 +263,21 @@ public async Task TestChecksumAlgorithm() Assert.All(project.AdditionalDocuments, d => Assert.Equal(SourceHashAlgorithm.Sha1, d.GetTextSynchronously(default).ChecksumAlgorithm)); } - [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] - public async Task TestChecksumAlgorithm_Default() + [ConditionalTheory(typeof(VisualStudioMSBuildInstalled))] + [InlineData(LanguageNames.CSharp)] + [InlineData(LanguageNames.VisualBasic)] + public async Task TestChecksumAlgorithm_Default(string language) { CreateFiles(GetMultiProjectSolutionFiles()); var solutionFilePath = GetSolutionFileName("TestSolution.sln"); using var workspace = CreateMSBuildWorkspace(); var sol = await workspace.OpenSolutionAsync(solutionFilePath); - var csProject = sol.Projects.First(p => p.Language == LanguageNames.CSharp); - var vbProject = sol.Projects.First(p => p.Language == LanguageNames.VisualBasic); + var project = sol.Projects.First(p => p.Language == language); - Assert.Equal(SourceHashAlgorithms.Default, csProject.State.ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithms.Default, vbProject.State.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, project.State.ChecksumAlgorithm); - Assert.All(csProject.Documents, d => Assert.Equal(SourceHashAlgorithms.Default, d.GetTextSynchronously(default).ChecksumAlgorithm)); - Assert.All(vbProject.Documents, d => Assert.Equal(SourceHashAlgorithms.Default, d.GetTextSynchronously(default).ChecksumAlgorithm)); + Assert.All(project.Documents, d => Assert.Equal(SourceHashAlgorithms.Default, d.GetTextSynchronously(default).ChecksumAlgorithm)); } [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index abab7a2f5aa83..343991df88700 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -300,7 +300,8 @@ private async Task UpdateProjectInfoAsync(Project project, Checksum inf Contract.ThrowIfFalse(project.State.ProjectInfo.Attributes.Language == newProjectAttributes.Language); Contract.ThrowIfFalse(project.State.ProjectInfo.Attributes.IsSubmission == newProjectAttributes.IsSubmission); - return project.Solution.WithProjectAttributes(project.Id, newProjectAttributes).GetRequiredProject(project.Id); + // do not propagate attribute update to documents - they will be updated by UpdateDocumentsAsync: + return project.Solution.WithProjectAttributes(project.Id, newProjectAttributes, updateDocuments: false).GetRequiredProject(project.Id); } private async Task UpdateDocumentsAsync( From b37f4f43416c2866d9f8034ec830f2fe74abdb4d Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 19 Sep 2022 11:54:49 -0700 Subject: [PATCH 05/32] Feedback --- .../ProjectFiles/CSharp/AdditionalFile.csproj | 3 +- .../CSharp/WithChecksumAlgorithm.csproj | 60 +++++++++ .../VisualBasic/WithChecksumAlgorithm.vbproj | 114 ++++++++++++++++++ .../MSBuildTest/TestFiles/Resources.cs | 2 + .../VisualStudioMSBuildWorkspaceTests.cs | 12 +- .../MSBuildTest/WorkspaceTestBase.cs | 14 +++ 6 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/WithChecksumAlgorithm.csproj create mode 100644 src/Workspaces/MSBuildTest/Resources/ProjectFiles/VisualBasic/WithChecksumAlgorithm.vbproj diff --git a/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj index 67d850f7b397f..1bb40b6b4f396 100644 --- a/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj +++ b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/AdditionalFile.csproj @@ -12,8 +12,7 @@ Properties CSharpProject CSharpProject - v4.8 - SHA1 + v4.8 512 snKey.snk diff --git a/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/WithChecksumAlgorithm.csproj b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/WithChecksumAlgorithm.csproj new file mode 100644 index 0000000000000..76d9f2880710b --- /dev/null +++ b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/CSharp/WithChecksumAlgorithm.csproj @@ -0,0 +1,60 @@ + + + + + + Debug + AnyCPU + AnyCPU + {9705A8E6-C854-4FD3-8CEA-EDBDD54C7FD8} + Exe + Properties + ConsoleApplication62 + ConsoleApplication62 + v4.8 + SHA1 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Workspaces/MSBuildTest/Resources/ProjectFiles/VisualBasic/WithChecksumAlgorithm.vbproj b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/VisualBasic/WithChecksumAlgorithm.vbproj new file mode 100644 index 0000000000000..945b50c2af4a9 --- /dev/null +++ b/src/Workspaces/MSBuildTest/Resources/ProjectFiles/VisualBasic/WithChecksumAlgorithm.vbproj @@ -0,0 +1,114 @@ + + + + + + Debug + AnyCPU + AnyCPU + {5D92B789-04AE-4B5C-A550-4E13378FA18F} + Exe + ConsoleApplication1.Module1 + ConsoleApplication1 + ConsoleApplication1 + SHA1 + Console + v4.8 + + + true + full + true + true + bin\Debug\ + ConsoleApplication1.xml + $(NoWarn);42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + ConsoleApplication1.xml + $(NoWarn);42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + On + + + Binary + + + Off + + + On + + + + + + + + + + + + + + + + + + + + + + + + True + Application.myapp + + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + \ No newline at end of file diff --git a/src/Workspaces/MSBuildTest/TestFiles/Resources.cs b/src/Workspaces/MSBuildTest/TestFiles/Resources.cs index 31cfb2b0c5509..c9bba22b196ef 100644 --- a/src/Workspaces/MSBuildTest/TestFiles/Resources.cs +++ b/src/Workspaces/MSBuildTest/TestFiles/Resources.cs @@ -160,6 +160,7 @@ public static class CSharp public static string WithoutCSharpTargetsImported => GetText("ProjectFiles.CSharp.WithoutCSharpTargetsImported.csproj"); public static string WithDiscoverEditorConfigFiles => GetText("ProjectFiles.CSharp.WithDiscoverEditorConfigFiles.csproj"); public static string WithPrefer32Bit => GetText("ProjectFiles.CSharp.WithPrefer32Bit.csproj"); + public static string WithChecksumAlgorithm => GetText("ProjectFiles.CSharp.WithChecksumAlgorithm.csproj"); public static string WithLink => GetText("ProjectFiles.CSharp.WithLink.csproj"); public static string WithSystemNumerics => GetText("ProjectFiles.CSharp.WithSystemNumerics.csproj"); public static string WithXaml => GetText("ProjectFiles.CSharp.WithXaml.csproj"); @@ -186,6 +187,7 @@ public static class VisualBasic public static string VisualBasicProject => GetText("ProjectFiles.VisualBasic.VisualBasicProject.vbproj"); public static string VisualBasicProject_3_5 => GetText("ProjectFiles.VisualBasic.VisualBasicProject_3_5.vbproj"); public static string WithPrefer32Bit => GetText("ProjectFiles.VisualBasic.WithPrefer32Bit.vbproj"); + public static string WithChecksumAlgorithm => GetText("ProjectFiles.VisualBasic.WithChecksumAlgorithm.vbproj"); public static string WithoutPrefer32Bit => GetText("ProjectFiles.VisualBasic.WithoutPrefer32Bit.vbproj"); public static string WithoutVBTargetsImported => GetText("ProjectFiles.VisualBasic.WithoutVBTargetsImported.vbproj"); public static string VBNetCoreAppWithGlobalImportAndLibrary_VBProject => GetText("VBNetCoreAppWithGlobalImportAndLibrary.VBProject.vbproj"); diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 8c48f585775a1..de76bba8839a5 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -247,10 +247,16 @@ public async Task TestCompilationOutputInfo() Assert.Equal("VisualBasicProject.dll", Path.GetFileName(p2.CompilationOutputInfo.AssemblyPath)); } - [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] - public async Task TestChecksumAlgorithm() + [ConditionalTheory(typeof(VisualStudioMSBuildInstalled))] + [InlineData(LanguageNames.CSharp)] + [InlineData(LanguageNames.VisualBasic)] + public async Task TestChecksumAlgorithm_NonDefault(string language) { - CreateFiles(GetSimpleCSharpSolutionWithAdditionaFile()); + var files = language == LanguageNames.CSharp ? + GetSimpleCSharpSolutionFiles().WithFile(@"CSharpProject\CSharpProject.csproj", Resources.ProjectFiles.CSharp.WithChecksumAlgorithm) : + GetSimpleVisualBasicSolutionFiles().WithFile(@"VisualBasicProject\VisualBasicProject.csproj", Resources.ProjectFiles.VisualBasic.WithChecksumAlgorithm); + + CreateFiles(files); var solutionFilePath = GetSolutionFileName("TestSolution.sln"); using var workspace = CreateMSBuildWorkspace(); diff --git a/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs b/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs index 8a6c74f4292bd..1991787c60ae9 100644 --- a/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs +++ b/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs @@ -104,6 +104,20 @@ protected static FileSet GetSimpleCSharpSolutionFiles() (@"CSharpProject\Properties\AssemblyInfo.cs", Resources.SourceFiles.CSharp.AssemblyInfo)); } + protected static FileSet GetSimpleVisualBasicSolutionFiles() + { + return new FileSet( + (@"VisualBasicProject\VisualBasicProject.vbproj", Resources.ProjectFiles.VisualBasic.VisualBasicProject), + (@"VisualBasicProject\VisualBasicClass.vb", Resources.SourceFiles.VisualBasic.VisualBasicClass), + (@"VisualBasicProject\My Project\Application.Designer.vb", Resources.SourceFiles.VisualBasic.Application_Designer), + (@"VisualBasicProject\My Project\Application.myapp", Resources.SourceFiles.VisualBasic.Application), + (@"VisualBasicProject\My Project\AssemblyInfo.vb", Resources.SourceFiles.VisualBasic.AssemblyInfo), + (@"VisualBasicProject\My Project\Resources.Designer.vb", Resources.SourceFiles.VisualBasic.Resources_Designer), + (@"VisualBasicProject\My Project\Resources.resx", Resources.SourceFiles.VisualBasic.Resources), + (@"VisualBasicProject\My Project\Settings.Designer.vb", Resources.SourceFiles.VisualBasic.Settings_Designer), + (@"VisualBasicProject\My Project\Settings.settings", Resources.SourceFiles.VisualBasic.Settings)); + } + protected static FileSet GetSimpleCSharpSolutionWithAdditionaFile() { return new FileSet( From 9e3d91d34328aff24a54875f7dcbe1b857bdf7fb Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 19 Sep 2022 14:58:48 -0700 Subject: [PATCH 06/32] fixes --- src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt | 2 -- .../EditAndContinueMethodDebugInfoReaderTests.cs | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index e11a70d68043a..49f1b24e6918f 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -20,8 +20,6 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ScopedType(Microsoft.CodeAnal virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> TResult? *REMOVED*static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options = null, string? path = "", System.Text.Encoding? encoding = null) -> Microsoft.CodeAnalysis.SyntaxTree! -static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options = null, string? path = "", System.Text.Encoding? encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) -> Microsoft.CodeAnalysis.SyntaxTree! static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options, string? path, System.Text.Encoding? encoding) -> Microsoft.CodeAnalysis.SyntaxTree! *REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options = null, string! path = "", System.Text.Encoding? encoding = null) -> Microsoft.CodeAnalysis.SyntaxTree! -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options = null, string! path = "", System.Text.Encoding? encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) -> Microsoft.CodeAnalysis.SyntaxTree! static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options, string! path, System.Text.Encoding? encoding) -> Microsoft.CodeAnalysis.SyntaxTree! diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs index 76fc9c17425f0..a6c802e7ab9ee 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueMethodDebugInfoReaderTests.cs @@ -61,7 +61,8 @@ public static void Main() } } "; - var compilation = CSharpTestBase.CreateCompilationWithMscorlib40AndSystemCore(source, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.DebugDll, sourceFileName: "/a/c.cs"); + var tree = CSharpTestSource.Parse(source, path: "/a/c.cs", options: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), checksumAlgorithm: SourceHashAlgorithm.Sha1); + var compilation = CSharpTestBase.CreateCompilationWithMscorlib40AndSystemCore(tree, options: TestOptions.DebugDll); var pdbStream = new MemoryStream(); compilation.EmitToArray(new EmitOptions(debugInformationFormat: format), pdbStream: pdbStream); From 3af3bed7f7fe397ae51f12a35d3d8383fcf7f5a6 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 19 Sep 2022 15:04:19 -0700 Subject: [PATCH 07/32] Revert test fix --- .../Utilities/VisualStudioMSBuildInstalled.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs b/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs index 96aed348776b8..3e0f0819c4b85 100644 --- a/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs +++ b/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using System.Linq; using Microsoft.Build.Locator; using Roslyn.Test.Utilities; @@ -18,20 +17,16 @@ internal class VisualStudioMSBuildInstalled : ExecutionCondition static VisualStudioMSBuildInstalled() { - var latestInstalledInstance = (VisualStudioInstance?)null; - foreach (var visualStudioInstance in MSBuildLocator.QueryVisualStudioInstances()) + var installedVisualStudios = MSBuildLocator.QueryVisualStudioInstances().ToArray(); + foreach (var visualStudioInstall in installedVisualStudios) { - if (latestInstalledInstance == null || visualStudioInstance.Version > latestInstalledInstance.Version) + if (visualStudioInstall.Version.Major == 17 && + visualStudioInstall.Version.Minor == 0) { - latestInstalledInstance = visualStudioInstance; + MSBuildLocator.RegisterInstance(visualStudioInstall); + s_instance = visualStudioInstall; } } - - if (latestInstalledInstance != null) - { - MSBuildLocator.RegisterInstance(latestInstalledInstance); - s_instance = latestInstalledInstance; - } } #endif From 39bc297daaeebafe72bfe75b3bd8e7e1f2955851 Mon Sep 17 00:00:00 2001 From: tmat Date: Tue, 20 Sep 2022 12:00:40 -0700 Subject: [PATCH 08/32] Fix --- src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 49f1b24e6918f..72a5aa6c898a5 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -19,7 +19,3 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ScopedType(Microsoft.CodeAnal static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ScopedType(Microsoft.CodeAnalysis.SyntaxToken scopedKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type) -> Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> TResult? -*REMOVED*static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options = null, string? path = "", System.Text.Encoding? encoding = null) -> Microsoft.CodeAnalysis.SyntaxTree! -static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode! root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options, string? path, System.Text.Encoding? encoding) -> Microsoft.CodeAnalysis.SyntaxTree! -*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options = null, string! path = "", System.Text.Encoding? encoding = null) -> Microsoft.CodeAnalysis.SyntaxTree! -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode! root, Microsoft.CodeAnalysis.ParseOptions? options, string! path, System.Text.Encoding? encoding) -> Microsoft.CodeAnalysis.SyntaxTree! From 9aaa01e98745578cb0f8eb6bb90e73c48f352a7a Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 22 Sep 2022 10:36:51 -0700 Subject: [PATCH 09/32] Ban more overloads of SyntaxTree.Create --- eng/config/BannedSymbols.txt | 2 ++ .../Portable/Syntax/CSharpSyntaxTree.Dummy.cs | 4 ++-- .../Portable/Syntax/CSharpSyntaxTree.cs | 19 +++++++++++++++++++ .../CSharp/Portable/Syntax/SyntaxFactory.cs | 4 +++- .../Semantics/TopLevelStatementsTests.cs | 12 +++++++----- .../VisualBasicSyntaxTree.DummySyntaxTree.vb | 4 ++-- .../Portable/Syntax/VisualBasicSyntaxTree.vb | 16 ++++++++++++++++ .../Test/Workspaces/TextFactoryTests.cs | 1 + .../MetadataAsSourceGeneratedFileInfo.cs | 1 - ...rceDocumentMetadataAsSourceFileProvider.cs | 8 +++++--- .../CoreTest/SolutionTests/SolutionTests.cs | 4 +--- 11 files changed, 58 insertions(+), 17 deletions(-) diff --git a/eng/config/BannedSymbols.txt b/eng/config/BannedSymbols.txt index 5f783bc5534a6..b47447f7ecde3 100644 --- a/eng/config/BannedSymbols.txt +++ b/eng/config/BannedSymbols.txt @@ -39,6 +39,7 @@ M:Microsoft.CodeAnalysis.FileTextLoader.#ctor(System.String,System.Text.Encoding M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken) M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode); Use CSharpSyntaxTree.Create +M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions options, System.String path, System.Text.Encoding encoding); Use overload with checksum algorithm M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.CSharp.CSharpParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken); Use API that takes SourceText M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.CSharp.CSharpParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Nullable{System.Boolean},System.Threading.CancellationToken); Use API that takes SourceText M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm @@ -46,4 +47,5 @@ M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(System.String M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Threading.CancellationToken); Use overload with SourceText M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode); Use VisualBasicSyntaxTree.Create M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.Create(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic}); Use overload with checksum algorithm +M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.Create(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Threading.CancellationToken); Use overload with SourceText diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs index 20b055565188f..85f2617d57600 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs @@ -92,10 +92,10 @@ public override bool HasCompilationUnitRoot } public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options) - => Create((CSharpSyntaxNode)root, (CSharpParseOptions)options, FilePath, Encoding); + => Create((CSharpSyntaxNode)root, (CSharpParseOptions)options, FilePath, Encoding, ChecksumAlgorithm); public override SyntaxTree WithFilePath(string path) - => Create(_node, Options, path, Encoding); + => Create(_node, Options, path, Encoding, ChecksumAlgorithm); } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs index 1a158e7a5385f..72bcd281c20d6 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs @@ -354,6 +354,25 @@ public static SyntaxTree Create( cloneRoot: true); } + internal static SyntaxTree Create( + CSharpSyntaxNode root, + CSharpParseOptions options, + string? path, + Encoding? encoding, + SourceHashAlgorithm checksumAlgorithm) + { + return new ParsedSyntaxTree( + textOpt: null, + encodingOpt: encoding, + checksumAlgorithm: checksumAlgorithm, + path: path, + options: options, + root: root, + directives: default, + diagnosticOptions: null, + cloneRoot: true); + } + /// /// Creates a new syntax tree from a syntax node with text that should correspond to the syntax node. /// diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index 36dbc8e915dd2..b11a471db811a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1545,7 +1545,9 @@ public static IdentifierNameSyntax IdentifierName(string name) /// public static SyntaxTree SyntaxTree(SyntaxNode root, ParseOptions? options = null, string path = "", Encoding? encoding = null) { +#pragma warning disable RS0030 // Do not used banned APIs return CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions?)options, path, encoding); +#pragma warning restore } #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters @@ -1561,7 +1563,7 @@ public static SyntaxTree ParseSyntaxTree( { return CSharpSyntaxTree.ParseText(text, (CSharpParseOptions?)options, path, encoding, cancellationToken); } -#pragma warning restore RS0030 // Do not used banned APIs +#pragma warning restore /// public static SyntaxTree ParseSyntaxTree( diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs index 98065b0ae78f7..31e6832a75357 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Text; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -16,6 +17,7 @@ using Microsoft.CodeAnalysis.FlowAnalysis; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; @@ -8673,13 +8675,13 @@ Task On01234567890123456() Task.WhenAll(this.c01234567890123456789.Select(v01234567 => v01234567.U0123456789012345678901234()))); "; - var newText = Microsoft.CodeAnalysis.Text.StringText.From(text2, System.Text.Encoding.UTF8); - using var lexer = new Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.Lexer(newText, TestOptions.RegularDefault); - using var parser = new Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.LanguageParser(lexer, - (CSharpSyntaxNode)oldTree.GetRoot(), new[] { new Microsoft.CodeAnalysis.Text.TextChangeRange(new Microsoft.CodeAnalysis.Text.TextSpan(282, 0), 1) }); + var newText = SourceText.From(text2, Encoding.UTF8, SourceHashAlgorithms.Default); + using var lexer = new Syntax.InternalSyntax.Lexer(newText, TestOptions.RegularDefault); + using var parser = new Syntax.InternalSyntax.LanguageParser(lexer, + (CSharpSyntaxNode)oldTree.GetRoot(), new[] { new TextChangeRange(new TextSpan(282, 0), 1) }); var compilationUnit = (CompilationUnitSyntax)parser.ParseCompilationUnit().CreateRed(); - var tree = CSharpSyntaxTree.Create(compilationUnit, TestOptions.RegularDefault, encoding: System.Text.Encoding.UTF8); + var tree = CSharpSyntaxTree.Create(compilationUnit, TestOptions.RegularDefault, path: "", Encoding.UTF8, SourceHashAlgorithms.Default); Assert.Equal(text2, tree.GetText().ToString()); tree.VerifySource(); diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb index 0428ad1705927..a292015795286 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.DummySyntaxTree.vb @@ -88,11 +88,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Property Public Overrides Function WithRootAndOptions(root As SyntaxNode, options As ParseOptions) As SyntaxTree - Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), FilePath, Me.Encoding) + Return Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), FilePath, Encoding, ChecksumAlgorithm) End Function Public Overrides Function WithFilePath(path As String) As SyntaxTree - Return VisualBasicSyntaxTree.Create(_node, Options, path, Me.Encoding) + Return Create(_node, Options, path, Encoding, ChecksumAlgorithm) End Function Public Overrides Function WithDiagnosticOptions(options As ImmutableDictionary(Of String, ReportDiagnostic)) As SyntaxTree diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb index 742dde6152601..ad16c66e37b07 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb @@ -177,6 +177,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic diagnosticOptions) End Function + Friend Shared Function Create(root As VisualBasicSyntaxNode, + options As VisualBasicParseOptions, + path As String, + encoding As Encoding, + checksumAlgorithm As SourceHashAlgorithm) As SyntaxTree + Return New ParsedSyntaxTree( + textOpt:=Nothing, + encodingOpt:=encoding, + checksumAlgorithm:=checksumAlgorithm, + path:=path, + options:=options, + syntaxRoot:=root, + isMyTemplate:=False, + diagnosticOptions:=Nothing) + End Function + ''' ''' Creates a new syntax tree from a syntax node with text that should correspond to the syntax node. ''' diff --git a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs index e4cfb8c9cf9da..4533f3c24a71e 100644 --- a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs @@ -122,6 +122,7 @@ private static void TestCreateTextInferredEncoding(ITextFactoryService textFacto using var stream = new MemoryStream(bytes); var text = textFactoryService.CreateText(stream, defaultEncoding, SourceHashAlgorithms.Default, CancellationToken.None); Assert.Equal(expectedEncoding, text.Encoding); + Assert.Equal(SourceHashAlgorithms.Default, text.ChecksumAlgorithm); } } } diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index a088026c38196..956159250953e 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -46,7 +46,6 @@ public MetadataAsSourceGeneratedFileInfo(string rootPath, Workspace sourceWorksp this.TemporaryFilePath = Path.Combine(rootPath, directoryName, topLevelNamedType.Name + extension); } - // TODO: public static Encoding Encoding => Encoding.UTF8; public static SourceHashAlgorithm ChecksumAlgorithm => SourceHashAlgorithms.Default; diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 5b0efb788bf5d..5402027cfeb2d 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -172,8 +172,6 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( } } - Debug.Assert(!sourceDocuments.IsEmpty); - var checksumAlgorithm = sourceDocuments[0].ChecksumAlgorithm; Encoding? defaultEncoding = null; if (pdbCompilationOptions.TryGetValue(Cci.CompilationOptionNames.DefaultEncoding, out var encodingString)) @@ -187,8 +185,12 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( if (!_assemblyToProjectMap.TryGetValue(assemblyName, out var projectId)) { + // Use the first document's checksum algorithm as a default, project-level value. + // The compiler doesn't persist the actual value of /checksumalgorithm in the PDB. + var projectChecksumAlgorithm = sourceDocuments[0].ChecksumAlgorithm; + // Get the project info now, so we can dispose the documentDebugInfoReader sooner - var projectInfo = CreateProjectInfo(metadataWorkspace, sourceProject, pdbCompilationOptions, assemblyName, assemblyVersion, checksumAlgorithm); + var projectInfo = CreateProjectInfo(metadataWorkspace, sourceProject, pdbCompilationOptions, assemblyName, assemblyVersion, projectChecksumAlgorithm); if (projectInfo is null) return null; diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index dd23321dade89..4903be21b379b 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Drawing.Drawing2D; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -32,10 +31,9 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using static Microsoft.CodeAnalysis.UnitTests.SolutionTestHelpers; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; -using static Microsoft.CodeAnalysis.UnitTests.SolutionTestHelpers; -using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; namespace Microsoft.CodeAnalysis.UnitTests { From 5ee1ef09b001e6a7d1e9065f8bdb76c1a4092cd2 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 23 Sep 2022 10:44:56 -0700 Subject: [PATCH 10/32] Merge --- .../MetadataAsSourceGeneratedFileInfo.cs | 2 +- .../Lsif/Generator/CompilerInvocation.cs | 2 +- .../Implementation/AbstractEditorFactory.cs | 2 +- .../PreviewUpdater.PreviewDialogWorkspace.cs | 2 +- .../MiscellaneousFilesWorkspace.cs | 2 +- ...tudioProject.BatchingDocumentCollection.cs | 4 ++-- .../RoslynRemoteProjectInfoProvider.cs | 2 +- .../WorkspaceFileTextLoaderNoException.cs | 10 ++++++---- .../Client/RemoteLanguageServiceWorkspace.cs | 4 ++-- .../Workspace/Solution/FileTextLoader.cs | 8 -------- .../Solution/LoadableTextAndVersionSource.cs | 16 ++++------------ .../Workspace/Solution/NullTextLoader.cs | 2 +- .../Solution/RecoverableTextAndVersion.cs | 16 ++++++---------- .../Workspace/Solution/TextDocumentState.cs | 19 ++++++++++--------- .../Portable/Workspace/Solution/TextLoader.cs | 2 +- .../Workspace/WorkspaceFileTextLoader.cs | 8 +++----- 16 files changed, 41 insertions(+), 60 deletions(-) diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index 956159250953e..7316872b7ad6a 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -85,7 +85,7 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work var generatedDocument = DocumentInfo.Create( generatedDocumentId, Path.GetFileName(TemporaryFilePath), - loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding, ChecksumAlgorithm) : null); + loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding, ChecksumAlgorithm) : null, filePath: TemporaryFilePath, isGenerated: true).WithDesignTimeOnly(true); diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 7d7adafff54d3..3fe17c26fba09 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -125,7 +125,7 @@ DocumentInfo CreateDocumentInfo(string unmappedPath) DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, - loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding, parsedCommandLine.ChecksumAlgorithm))); + loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding, parsedCommandLine.ChecksumAlgorithm)); } } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 3de6038f002c0..51b0f4195ed21 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -326,7 +326,7 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy var documentId = DocumentId.CreateNewId(projectToAddTo.Id); - var fileLoader = new WorkspaceFileTextLoader(filePath, defaultEncoding: null, projectToAddTo.State.ChecksumAlgorithm); + var fileLoader = new WorkspaceFileTextLoader(solution.Services, filePath, defaultEncoding: null, projectToAddTo.State.ChecksumAlgorithm); var forkedSolution = projectToAddTo.Solution.AddDocument( DocumentInfo.Create( documentId, diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index 50cecb3c2e1c2..cdc7420393e2e 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs @@ -54,7 +54,7 @@ private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashA => throw ExceptionUtilities.Unreachable; // checksum alg should never be changed in preview workspace public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); + => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) => TextAndVersion.Create(_text, VersionStamp.Create()); diff --git a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs index c27a7e69e5d23..3f997ee136abe 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -293,7 +293,7 @@ private ProjectInfo CreateProjectInfoForDocument(string filePath) Contract.ThrowIfNull(languageInformation); var checksumAlgorithm = SourceHashAlgorithms.Default; - var fileLoader = new WorkspaceFileTextLoader(filePath, defaultEncoding: null, checksumAlgorithm); + var fileLoader = new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null, checksumAlgorithm); return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, fileLoader, languageInformation, checksumAlgorithm, Services.SolutionServices, _metadataReferences); } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index c8a00d58ca274..e905f7cb8248e 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -92,7 +92,7 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta } var documentId = DocumentId.CreateNewId(_project.Id, fullPath); - var textLoader = new WorkspaceFileTextLoader(fullPath, defaultEncoding: null, _project.ChecksumAlgorithm); + var textLoader = new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, fullPath, defaultEncoding: null, _project.ChecksumAlgorithm); var documentInfo = DocumentInfo.Create( documentId, name: FileNameUtilities.GetFileName(fullPath), @@ -405,7 +405,7 @@ public async ValueTask ProcessRegularFileChangesAsync(ImmutableSegmentedList d.Id == documentId)) { - documentsToChange.Add((documentId, new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, filePath, defaultEncoding: null))); + documentsToChange.Add((documentId, new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, filePath, defaultEncoding: null, _project.ChecksumAlgorithm))); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs index 0274c1cbc0563..cdfe355a53d2d 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs @@ -93,7 +93,7 @@ private static ProjectInfo CreateProjectInfo(string projectName, string language DocumentInfo.Create( DocumentId.CreateNewId(projectId), name: Path.GetFileNameWithoutExtension(path), - loader: new WorkspaceFileTextLoaderNoException(path, defaultEncoding: null, checksumAlgorithm), + loader: new WorkspaceFileTextLoaderNoException(services, path, defaultEncoding: null, checksumAlgorithm), filePath: path)); return ProjectInfo.Create( diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs index 0c2ebe8ff0f7a..55737498bb923 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs @@ -18,12 +18,14 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects /// This is a FileTextLoader which no-ops if the file is not available on disk. This is the common case for /// Cascade and throwing exceptions slows down GetText operations significantly enough to have visible UX impact. /// -<<<<<<< HEAD:src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs internal sealed class WorkspaceFileTextLoaderNoException : WorkspaceFileTextLoader { - public WorkspaceFileTextLoaderNoException(string path, Encoding defaultEncoding, SourceHashAlgorithm checksumAlgorithm) - : base(path, defaultEncoding, checksumAlgorithm) + private readonly SolutionServices _services; + + public WorkspaceFileTextLoaderNoException(SolutionServices services, string path, Encoding defaultEncoding, SourceHashAlgorithm checksumAlgorithm) + : base(services, path, defaultEncoding, checksumAlgorithm) { + _services = services; } public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) @@ -37,6 +39,6 @@ public override Task LoadTextAndVersionAsync(CancellationToken c } private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) - => new FileTextLoaderNoException(Path, DefaultEncoding, algorithm); + => new WorkspaceFileTextLoaderNoException(_services, Path, DefaultEncoding, algorithm); } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index 67edb3b48f409..14c018d3ddd93 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -348,7 +348,7 @@ private Document AddDocumentToProject(string filePath, string language, string p var docInfo = DocumentInfo.Create( DocumentId.CreateNewId(project.Id), name: Path.GetFileName(filePath), - loader: new WorkspaceFileTextLoader(filePath, defaultEncoding: null, project.State.ChecksumAlgorithm), + loader: new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null, project.State.ChecksumAlgorithm), filePath: filePath); OnDocumentAdded(docInfo); @@ -383,7 +383,7 @@ public void NotifyOnDocumentClosing(string moniker) // check if the doc is part of the current Roslyn workspace before notifying Roslyn. if (CurrentSolution.ContainsProject(id.ProjectId)) { - OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(moniker, null, SourceHashAlgorithms.Default)); + OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(Services.SolutionServices, moniker, null, SourceHashAlgorithms.Default)); _openedDocs = _openedDocs.Remove(moniker); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs index cd046edbedac9..43d9f09a1154c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs @@ -85,7 +85,6 @@ private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashA internal sealed override string FilePath => Path; -<<<<<<< HEAD:src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs /// /// Creates from . /// @@ -102,13 +101,6 @@ protected virtual SourceText CreateText(Stream stream, CancellationToken cancell #pragma warning disable CS0618 // Type or member is obsolete => CreateText(stream, workspace: null); #pragma warning restore -======= - protected virtual SourceText CreateText(Stream stream, Workspace workspace) - { - var factory = workspace.Services.GetRequiredService(); - return factory.CreateText(stream, DefaultEncoding, ChecksumAlgorithm, CancellationToken.None); - } ->>>>>>> b316947cf73 (Add ChecksumAlgorithm to DocumentAttributes and ProjectAttributes):src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs /// /// Load a text and a version of the document in the workspace. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs index a20ceb64318f4..005ff34ba7bad 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs @@ -15,23 +15,18 @@ internal sealed class LoadableTextAndVersionSource : ValueSource { private readonly AsyncLazy _lazy; private readonly TextLoader _loader; - private readonly DocumentId _documentId; - private readonly Workspace _workspace; - public LoadableTextAndVersionSource(TextLoader loader, DocumentId documentId, Workspace workspace, bool cacheResult) + public LoadableTextAndVersionSource(TextLoader loader, bool cacheResult) { _loader = loader; - _documentId = documentId; - _workspace = workspace; - _lazy = new AsyncLazy(LoadAsync, LoadSynchronously, cacheResult); } private Task LoadAsync(CancellationToken cancellationToken) - => _loader.LoadTextAsync(_workspace, _documentId, cancellationToken); + => _loader.LoadTextAsync(cancellationToken); private TextAndVersion LoadSynchronously(CancellationToken cancellationToken) - => _loader.LoadTextSynchronously(_workspace, _documentId, cancellationToken); + => _loader.LoadTextSynchronously(cancellationToken); public override TextAndVersion GetValue(CancellationToken cancellationToken) => _lazy.GetValue(cancellationToken); @@ -45,9 +40,6 @@ public override Task GetValueAsync(CancellationToken cancellatio public TextLoader Loader => _loader; - public DocumentId DocumentId - => _documentId; - public SourceHashAlgorithm ChecksumAlgorithm => _loader.ChecksumAlgorithm; @@ -64,6 +56,6 @@ public SourceHashAlgorithm ChecksumAlgorithm return this; } - return new LoadableTextAndVersionSource(newLoader, _documentId, _workspace, _lazy.CacheResult); + return new LoadableTextAndVersionSource(newLoader, _lazy.CacheResult); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs index 2c1d50fbe5e68..f5b7668f8c5e1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs @@ -29,6 +29,6 @@ public NullTextLoader(SourceHashAlgorithm checksumAlgorithm) private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) => new NullTextLoader(algorithm); - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs index 07a30ce9a2a1e..ded793218e68b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -18,13 +18,13 @@ namespace Microsoft.CodeAnalysis /// internal sealed class RecoverableTextAndVersion : ValueSource, ITextVersionable, ITextAndVersionSource { - private readonly HostWorkspaceServices _services; + private readonly SolutionServices _services; // Starts as ITextAndVersionSource and is replaced with RecoverableText when the TextAndVersion value is requested. // At that point the initial source is no longer referenced and can be garbage collected. private object _initialSourceOrRecoverableText; - public RecoverableTextAndVersion(ITextAndVersionSource initialSource, HostWorkspaceServices services) + public RecoverableTextAndVersion(ITextAndVersionSource initialSource, SolutionServices services) { _initialSourceOrRecoverableText = initialSource; _services = services; @@ -90,7 +90,7 @@ public override TextAndVersion GetValue(CancellationToken cancellationToken = de // replace initial source with recovarable text if it hasn't been replaced already: Interlocked.CompareExchange( ref _initialSourceOrRecoverableText, - value: new RecoverableText(source, source.GetValue(cancellationToken), _services.SolutionServices), + value: new RecoverableText(source, source.GetValue(cancellationToken), _services), comparand: source); } @@ -105,7 +105,7 @@ public override async Task GetValueAsync(CancellationToken cance // replace initial source with recovarable text if it hasn't been replaced already: Interlocked.CompareExchange( ref _initialSourceOrRecoverableText, - value: new RecoverableText(source, await source.GetValueAsync(cancellationToken).ConfigureAwait(false), _services.SolutionServices), + value: new RecoverableText(source, await source.GetValueAsync(cancellationToken).ConfigureAwait(false), _services), comparand: source); } @@ -136,9 +136,7 @@ public SourceHashAlgorithm ChecksumAlgorithm return null; } - Contract.ThrowIfNull(recoverableText.DocumentId); - - return new RecoverableTextAndVersion(new LoadableTextAndVersionSource(newFileLoader, recoverableText.DocumentId, _services.Workspace, cacheResult: false), _services); + return new RecoverableTextAndVersion(new LoadableTextAndVersionSource(newFileLoader, cacheResult: false), _services); } private sealed class RecoverableText : WeaklyCachedRecoverableValueSource, ITextVersionable @@ -147,7 +145,6 @@ private sealed class RecoverableText : WeaklyCachedRecoverableValueSource new ConstantTextAndVersionSource(text); - private static ITextAndVersionSource CreateStrongText(TextLoader loader, DocumentId documentId, SolutionServices services) - => new LoadableTextAndVersionSource(loader, documentId, services, cacheResult: true); + private static ITextAndVersionSource CreateStrongText(TextLoader loader) + => new LoadableTextAndVersionSource(loader, cacheResult: true); - private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, HostWorkspaceServices services) + private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, SolutionServices services) { var result = new RecoverableTextAndVersion(new ConstantTextAndVersionSource(text), services); @@ -106,8 +107,8 @@ private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, return result; } - protected static ValueSource CreateRecoverableText(TextLoader loader, SolutionServices services) - => new RecoverableTextAndVersion(new LoadableTextAndVersionSource(loader, documentId, services, cacheResult: false), services); + private static ITextAndVersionSource CreateRecoverableText(TextLoader loader, SolutionServices services) + => new RecoverableTextAndVersion(new LoadableTextAndVersionSource(loader, cacheResult: false), services); public ITemporaryTextStorageInternal? Storage => (TextAndVersionSource as RecoverableTextAndVersion)?.Storage; @@ -202,7 +203,7 @@ public TextDocumentState UpdateText(TextAndVersion newTextAndVersion, Preservati { var newTextSource = mode == PreservationMode.PreserveIdentity ? CreateStrongText(newTextAndVersion) - : CreateRecoverableText(newTextAndVersion, solutionServices); + : CreateRecoverableText(newTextAndVersion, solutionServices.SolutionServices); return UpdateText(newTextSource, mode, incremental: true); } @@ -219,8 +220,8 @@ public TextDocumentState UpdateText(TextLoader loader, PreservationMode mode) { // don't blow up on non-text documents. var newTextSource = mode == PreservationMode.PreserveIdentity - ? CreateStrongText(loader, Id, solutionServices) - : CreateRecoverableText(loader, Id, solutionServices); + ? CreateStrongText(loader) + : CreateRecoverableText(loader, solutionServices.SolutionServices); return UpdateText(newTextSource, mode, incremental: false); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index edac004a5afe5..6c8d4d8f98052 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -228,7 +228,7 @@ internal override string? FilePath => _filePath; public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); + => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) => TextAndVersion.Create(_container.CurrentText, _version, _filePath); diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs index ca312116d805b..4133578f826d6 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs @@ -19,15 +19,13 @@ internal class WorkspaceFileTextLoader : FileTextLoader { private readonly ITextFactoryService _textFactory; - internal WorkspaceFileTextLoader(SolutionServices services, string path, Encoding? defaultEncoding) -#pragma warning disable RS0030 // Do not used banned APIs - : base(path, defaultEncoding) -#pragma warning restore + internal WorkspaceFileTextLoader(SolutionServices services, string path, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm) + : base(path, defaultEncoding, checksumAlgorithm) { _textFactory = services.GetRequiredService(); } protected override SourceText CreateText(Stream stream, CancellationToken cancellationToken) - => _textFactory.CreateText(stream, DefaultEncoding, cancellationToken); + => _textFactory.CreateText(stream, DefaultEncoding, ChecksumAlgorithm, cancellationToken); } } From 5d4770a33f0f3a3dce28dfaf5f09a1399726d95c Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 23 Sep 2022 14:03:26 -0700 Subject: [PATCH 11/32] LoadChecksum --- .../Core/Interactive/InteractiveSession.cs | 2 +- .../EditAndContinueWorkspaceServiceTests.cs | 18 ++-- .../Workspaces/TestHostDocument.cs | 7 +- ...compilationMetadataAsSourceFileProvider.cs | 2 +- .../MetadataAsSourceGeneratedFileInfo.cs | 2 +- ...rceDocumentMetadataAsSourceFileProvider.cs | 2 +- .../LspMiscellaneousFilesWorkspace.cs | 9 +- .../Lsif/Generator/CompilerInvocation.cs | 2 +- .../Implementation/AbstractEditorFactory.cs | 2 +- .../MiscellaneousFilesWorkspace.cs | 2 +- ...tudioProject.BatchingDocumentCollection.cs | 4 +- ...sualStudioWorkspaceImpl.OpenFileTracker.cs | 6 +- .../WorkspaceFileTextLoaderNoException.cs | 2 +- .../Client/RemoteLanguageServiceWorkspace.cs | 2 +- .../MSBuild/MSBuildProjectLoader.Worker.cs | 2 +- .../Core/MSBuild/MSBuild/MSBuildWorkspace.cs | 2 +- .../Core/Portable/PublicAPI.Unshipped.txt | 11 ++- .../Portable/Workspace/CommandLineProject.cs | 4 +- ...ryService.AbstractRecoverableSyntaxRoot.cs | 8 +- .../Solution/AdditionalDocumentState.cs | 8 +- .../Solution/AnalyzerConfigDocumentState.cs | 8 +- .../Solution/ConstantTextAndVersionSource.cs | 16 ++-- .../Workspace/Solution/DocumentInfo.cs | 19 ++-- .../Workspace/Solution/DocumentState.cs | 88 ++++++++++++------- .../Solution/DocumentState_TreeTextSource.cs | 13 ++- .../Workspace/Solution/FileTextLoader.cs | 37 ++------ .../Solution/ITextAndVersionSource.cs | 9 +- .../Workspace/Solution/ITextVersionable.cs | 2 +- .../Solution/LoadableTextAndVersionSource.cs | 75 +++++++++------- .../Workspace/Solution/NullTextLoader.cs | 13 +-- .../Workspace/Solution/ProjectState.cs | 3 - .../Solution/RecoverableTextAndVersion.cs | 84 +++++++++--------- .../SolutionState.CompilationTracker.cs | 2 +- .../Solution/SourceGeneratedDocumentState.cs | 7 +- .../Workspace/Solution/TextDocumentState.cs | 40 +++++---- .../Portable/Workspace/Solution/TextLoader.cs | 45 +++++----- .../Workspace/WorkspaceFileTextLoader.cs | 8 +- .../Portable/Workspace/Workspace_Editor.cs | 7 +- .../CoreTest/SolutionTests/SolutionTests.cs | 8 +- ...reeFactoryService.RecoverableSyntaxTree.vb | 12 +-- 40 files changed, 300 insertions(+), 293 deletions(-) diff --git a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs index 914e0f62ccd6f..f13654f4f4e5f 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs @@ -234,7 +234,7 @@ private void AddSubmissionProjectNoLock(ITextBuffer submissionBuffer, string lan solution = initProject.Solution.AddDocument( DocumentId.CreateNewId(initializationScriptProjectId, debugName: initializationScriptPath), Path.GetFileName(initializationScriptPath), - new WorkspaceFileTextLoader(solution.Services, initializationScriptPath, defaultEncoding: null, initProject.State.ChecksumAlgorithm)); + new WorkspaceFileTextLoader(solution.Services, initializationScriptPath, defaultEncoding: null)); } var newSubmissionProject = CreateSubmissionProjectNoLock(solution, _currentSubmissionProjectId, _lastSuccessfulSubmissionProjectId, languageName, imports, references); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 1ae3bd7416cd5..55da33c265383 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -374,7 +374,7 @@ private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, st internal sealed class FailingTextLoader : TextLoader { - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { Assert.True(false, $"Content of document should never be loaded"); throw ExceptionUtilities.Unreachable; @@ -498,28 +498,28 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA, SourceHashAlgorithm.Sha1), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA), filePath: sourceFileA.Path)); var documentIdB = DocumentId.CreateNewId(projectP.Id, debugName: "B"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdB, name: "B", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB, SourceHashAlgorithm.Sha1), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), filePath: sourceFileB.Path)); var documentIdC = DocumentId.CreateNewId(projectP.Id, debugName: "C"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdC, name: "C", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC, SourceHashAlgorithm.Sha1), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC), filePath: sourceFileC.Path)); var documentIdE = DocumentId.CreateNewId(projectP.Id, debugName: "E"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdE, name: "E", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE, SourceHashAlgorithm.Sha1), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE), filePath: sourceFileE.Path)); // check that are testing documents whose hash algorithm does not match the PDB (but the hash itself does): @@ -559,7 +559,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume // change content of B on disk again: sourceFileB.WriteAllText(sourceB3, encodingB); - solution = solution.WithDocumentTextLoader(documentIdB, new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB, SourceHashAlgorithms.Default), PreservationMode.PreserveValue); + solution = solution.WithDocumentTextLoader(documentIdB, new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), PreservationMode.PreserveValue); EnterBreakState(debuggingSession); @@ -4393,7 +4393,7 @@ public async Task MultiSession() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8, SourceHashAlgorithms.Default), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var tasks = Enumerable.Range(0, 10).Select(async i => @@ -4479,7 +4479,7 @@ public async Task WatchHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8, SourceHashAlgorithms.Default), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var hotReload = new WatchHotReloadService(workspace.Services, ImmutableArray.Create("Baseline", "AddDefinitionToExistingType", "NewTypeDefinition")); @@ -4546,7 +4546,7 @@ public async Task UnitTestingHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8, SourceHashAlgorithms.Default), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var hotReload = new UnitTestingHotReloadService(workspace.Services); diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index 22fbd70ae14b0..94d1b1213b607 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -210,14 +210,11 @@ internal TestDocumentLoader(TestHostDocument hostDocument, string text) _text = text; } - internal override SourceHashAlgorithm ChecksumAlgorithm - => _hostDocument.ChecksumAlgorithm; - internal override string? FilePath => _hostDocument.FilePath; - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(TextAndVersion.Create(SourceText.From(_text, encoding: null, _hostDocument.ChecksumAlgorithm), VersionStamp.Create(), _hostDocument.FilePath)); + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(TextAndVersion.Create(SourceText.From(_text, encoding: null, options.ChecksumAlgorithm), VersionStamp.Create(), _hostDocument.FilePath)); } public TextLoader Loader => _loader; diff --git a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs index 02a054c63e290..62dfa0e2d7122 100644 --- a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs @@ -279,7 +279,7 @@ private bool RemoveDocumentFromWorkspace(Workspace workspace, MetadataAsSourceGe var documentId = _openedDocumentIds.GetValueOrDefault(fileInfo); Contract.ThrowIfNull(documentId); - workspace.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding, MetadataAsSourceGeneratedFileInfo.ChecksumAlgorithm)); + workspace.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding)); workspace.OnProjectRemoved(documentId.ProjectId); _openedDocumentIds = _openedDocumentIds.RemoveKey(fileInfo); diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index 7316872b7ad6a..23880c67bd950 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -85,7 +85,7 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work var generatedDocument = DocumentInfo.Create( generatedDocumentId, Path.GetFileName(TemporaryFilePath), - loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding, ChecksumAlgorithm) : null, + loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding) : null, filePath: TemporaryFilePath, isGenerated: true).WithDesignTimeOnly(true); diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 5402027cfeb2d..362f97688f726 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -353,7 +353,7 @@ public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) { if (_fileToDocumentInfoMap.TryGetValue(filePath, out var info)) { - workspace.OnDocumentClosed(info.DocumentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, filePath, info.Encoding, info.ChecksumAlgorithm)); + workspace.OnDocumentClosed(info.DocumentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, filePath, info.Encoding)); return true; } diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 0c2d9f8fd8203..2eaf8d26d5d39 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -103,16 +103,11 @@ public SourceTextLoader(SourceText sourceText, string fileUri) _fileUri = fileUri; } - internal override SourceHashAlgorithm ChecksumAlgorithm - => _sourceText.ChecksumAlgorithm; - internal override string? FilePath => _fileUri; - private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) - => throw ExceptionUtilities.Unreachable; // TODO: https://github.com/dotnet/roslyn/issues/63583 - - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + // TODO (https://github.com/dotnet/roslyn/issues/63583): Use options.ChecksumAlgorithm + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_sourceText, VersionStamp.Create(), _fileUri)); } } diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 3fe17c26fba09..59e27ecf372a9 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -125,7 +125,7 @@ DocumentInfo CreateDocumentInfo(string unmappedPath) DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, - loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding, parsedCommandLine.ChecksumAlgorithm)); + loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding)); } } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 51b0f4195ed21..eeec5768a2007 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -326,7 +326,7 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy var documentId = DocumentId.CreateNewId(projectToAddTo.Id); - var fileLoader = new WorkspaceFileTextLoader(solution.Services, filePath, defaultEncoding: null, projectToAddTo.State.ChecksumAlgorithm); + var fileLoader = new WorkspaceFileTextLoader(solution.Services, filePath, defaultEncoding: null); var forkedSolution = projectToAddTo.Solution.AddDocument( DocumentInfo.Create( documentId, diff --git a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs index 3f997ee136abe..e1c97c134ad24 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -293,7 +293,7 @@ private ProjectInfo CreateProjectInfoForDocument(string filePath) Contract.ThrowIfNull(languageInformation); var checksumAlgorithm = SourceHashAlgorithms.Default; - var fileLoader = new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null, checksumAlgorithm); + var fileLoader = new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null); return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, fileLoader, languageInformation, checksumAlgorithm, Services.SolutionServices, _metadataReferences); } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index e905f7cb8248e..88452f03d577c 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -92,7 +92,7 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta } var documentId = DocumentId.CreateNewId(_project.Id, fullPath); - var textLoader = new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, fullPath, defaultEncoding: null, _project.ChecksumAlgorithm); + var textLoader = new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, fullPath, defaultEncoding: null); var documentInfo = DocumentInfo.Create( documentId, name: FileNameUtilities.GetFileName(fullPath), @@ -405,7 +405,7 @@ public async ValueTask ProcessRegularFileChangesAsync(ImmutableSegmentedList d.Id == documentId)) { - documentsToChange.Add((documentId, new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, filePath, defaultEncoding: null, _project.ChecksumAlgorithm))); + documentsToChange.Add((documentId, new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, filePath, defaultEncoding: null))); } } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs index 10b07935c2bcd..3d05831dbeede 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs @@ -323,16 +323,16 @@ private void TryClosingDocumentsForMoniker(string moniker) if (solution.GetDocument(documentId) is { } document) { - w.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null, document.State.ChecksumAlgorithm)); + w.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null)); } else if (solution.GetAdditionalDocument(documentId) is { } additionalDocument) { - w.OnAdditionalDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null, additionalDocument.State.ChecksumAlgorithm)); + w.OnAdditionalDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null)); } else { var analyzerConfigDocument = solution.GetRequiredAnalyzerConfigDocument(documentId); - w.OnAnalyzerConfigDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null, analyzerConfigDocument.State.ChecksumAlgorithm)); + w.OnAnalyzerConfigDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null)); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs index 55737498bb923..acd18d45b2277 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs @@ -23,7 +23,7 @@ internal sealed class WorkspaceFileTextLoaderNoException : WorkspaceFileTextLoad private readonly SolutionServices _services; public WorkspaceFileTextLoaderNoException(SolutionServices services, string path, Encoding defaultEncoding, SourceHashAlgorithm checksumAlgorithm) - : base(services, path, defaultEncoding, checksumAlgorithm) + : base(services, path, defaultEncoding) { _services = services; } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index 14c018d3ddd93..173b03349e6d9 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -348,7 +348,7 @@ private Document AddDocumentToProject(string filePath, string language, string p var docInfo = DocumentInfo.Create( DocumentId.CreateNewId(project.Id), name: Path.GetFileName(filePath), - loader: new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null, project.State.ChecksumAlgorithm), + loader: new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null), filePath: filePath); OnDocumentAdded(docInfo); diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index a7d83e16881ae..74b7f04078786 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -452,7 +452,7 @@ private ImmutableArray CreateDocumentInfos(IReadOnlyList bool Microsoft.CodeAnalysis.ISupportedChangesService.CanApplyCompilationOptionChange(Microsoft.CodeAnalysis.CompilationOptions oldOptions, Microsoft.CodeAnalysis.CompilationOptions newOptions, Microsoft.CodeAnalysis.Project project) -> bool Microsoft.CodeAnalysis.ISupportedChangesService.CanApplyParseOptionChange(Microsoft.CodeAnalysis.ParseOptions oldOptions, Microsoft.CodeAnalysis.ParseOptions newOptions, Microsoft.CodeAnalysis.Project project) -> bool +Microsoft.CodeAnalysis.LoadTextOptions +Microsoft.CodeAnalysis.LoadTextOptions.ChecksumAlgorithm.get -> Microsoft.CodeAnalysis.Text.SourceHashAlgorithm +Microsoft.CodeAnalysis.LoadTextOptions.ChecksumAlgorithm.init -> void +Microsoft.CodeAnalysis.LoadTextOptions.LoadTextOptions() -> void +Microsoft.CodeAnalysis.LoadTextOptions.LoadTextOptions(Microsoft.CodeAnalysis.Text.SourceHashAlgorithm ChecksumAlgorithm) -> void Microsoft.CodeAnalysis.Project.Services.get -> Microsoft.CodeAnalysis.Host.LanguageServices Microsoft.CodeAnalysis.Solution.Services.get -> Microsoft.CodeAnalysis.Host.SolutionServices Microsoft.CodeAnalysis.TextDocumentEventArgs @@ -28,10 +33,10 @@ Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentClosedEventAsync(Microsoft.Cod Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentOpenedEventAsync(Microsoft.CodeAnalysis.TextDocument document) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Workspace.TextDocumentClosed -> System.EventHandler Microsoft.CodeAnalysis.Workspace.TextDocumentOpened -> System.EventHandler +override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers -virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText -override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText *REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task *REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs index 01963a5228119..631d4255a002b 100644 --- a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs @@ -127,7 +127,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, name, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, - loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding, commandLineArguments.ChecksumAlgorithm), + loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), filePath: absolutePath); docs.Add(doc); @@ -154,7 +154,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, name: name, folders: folders, sourceCodeKind: SourceCodeKind.Regular, - loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding, commandLineArguments.ChecksumAlgorithm), + loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), filePath: absolutePath); additionalDocs.Add(doc); diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs index 4c574f4bd18e0..3f6e1c1221840 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs @@ -40,9 +40,9 @@ public SyntaxTreeInfo( ContainsDirectives = containsDirectives; } - internal bool TryGetText([NotNullWhen(true)] out SourceText? text) + internal bool TryGetText(LoadTextOptions options, [NotNullWhen(true)] out SourceText? text) { - if (TextSource.TryGetValue(out var textAndVersion)) + if (TextSource.TryGetValue(options, out var textAndVersion)) { text = textAndVersion.Text; return true; @@ -52,9 +52,9 @@ internal bool TryGetText([NotNullWhen(true)] out SourceText? text) return false; } - internal async Task GetTextAsync(CancellationToken cancellationToken) + internal async Task GetTextAsync(LoadTextOptions options, CancellationToken cancellationToken) { - var textAndVersion = await TextSource.GetValueAsync(cancellationToken).ConfigureAwait(false); + var textAndVersion = await TextSource.GetValueAsync(options, cancellationToken).ConfigureAwait(false); return textAndVersion.Text; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs index 3b5c98fd565f6..5eecf0d14cdb0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs @@ -18,8 +18,9 @@ private AdditionalDocumentState( IDocumentServiceProvider documentServiceProvider, DocumentInfo.DocumentAttributes attributes, SourceText? sourceText, - ITextAndVersionSource textAndVersionSource) - : base(solutionServices, documentServiceProvider, attributes, sourceText, textAndVersionSource) + ITextAndVersionSource textAndVersionSource, + LoadTextOptions loadTextOptions) + : base(solutionServices, documentServiceProvider, attributes, sourceText, textAndVersionSource, loadTextOptions) { _additionalText = new AdditionalTextWithState(this); } @@ -50,7 +51,8 @@ protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSou this.Services, this.Attributes, this.sourceText, - newTextSource); + newTextSource, + this.LoadTextOptions); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs index 15554ce2347d7..c8841240f06ef 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs @@ -21,8 +21,9 @@ private AnalyzerConfigDocumentState( IDocumentServiceProvider documentServiceProvider, DocumentInfo.DocumentAttributes attributes, SourceText sourceTextOpt, - ITextAndVersionSource textAndVersionSource) - : base(solutionServices, documentServiceProvider, attributes, sourceTextOpt, textAndVersionSource) + ITextAndVersionSource textAndVersionSource, + LoadTextOptions loadTextOptions) + : base(solutionServices, documentServiceProvider, attributes, sourceTextOpt, textAndVersionSource, loadTextOptions) { _analyzerConfigValueSource = CreateAnalyzerConfigValueSource(); } @@ -62,7 +63,8 @@ protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSou this.Services, this.Attributes, this.sourceText, - newTextSource); + newTextSource, + this.LoadTextOptions); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs index 441ea920d5f9d..5f29f9f42c9c8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -17,15 +20,18 @@ public ConstantTextAndVersionSource(TextAndVersion value) { } - public SourceHashAlgorithm ChecksumAlgorithm - => Value.Text.ChecksumAlgorithm; + public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) + => GetValue(cancellationToken); + + public Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) + => GetValueAsync(cancellationToken); + + public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) + => TryGetValue(out value); public bool TryGetTextVersion(out VersionStamp version) { version = Value.Version; return true; } - - public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) - => null; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 67cfc06717bac..963ce8f02747f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -21,6 +21,7 @@ namespace Microsoft.CodeAnalysis public sealed class DocumentInfo { private readonly TextLoader _textLoader; + internal readonly LoadTextOptions LoadTextOptions; internal DocumentAttributes Attributes { get; } @@ -62,10 +63,11 @@ public sealed class DocumentInfo /// /// Create a new instance of a . /// - internal DocumentInfo(DocumentAttributes attributes, TextLoader loader, IDocumentServiceProvider? documentServiceProvider) + internal DocumentInfo(DocumentAttributes attributes, TextLoader loader, LoadTextOptions loadTextOptions, IDocumentServiceProvider? documentServiceProvider) { Attributes = attributes; _textLoader = loader; + LoadTextOptions = loadTextOptions; DocumentServiceProvider = documentServiceProvider; } @@ -91,26 +93,30 @@ public static DocumentInfo Create( isGenerated, designTimeOnly: false), loader ?? NullTextLoader.Default, + new LoadTextOptions(SourceHashAlgorithm.Sha1), documentServiceProvider: null); } private DocumentInfo With( DocumentAttributes? attributes = null, TextLoader? loader = null, + Optional loadTextOptions = default, Optional documentServiceProvider = default) { var newAttributes = attributes ?? Attributes; var newLoader = loader ?? _textLoader; + var newLoadTextOptions = loadTextOptions.HasValue ? loadTextOptions.Value : LoadTextOptions; var newDocumentServiceProvider = documentServiceProvider.HasValue ? documentServiceProvider.Value : DocumentServiceProvider; if (newAttributes == Attributes && newLoader == _textLoader && + newLoadTextOptions == LoadTextOptions && newDocumentServiceProvider == DocumentServiceProvider) { return this; } - return new DocumentInfo(newAttributes, newLoader, newDocumentServiceProvider); + return new DocumentInfo(newAttributes, newLoader, newLoadTextOptions, newDocumentServiceProvider); } /// @@ -119,12 +125,6 @@ private DocumentInfo With( public TextLoader? TextLoader => _textLoader is NullTextLoader ? null : _textLoader; - /// - /// Algorithm used for calculating the document content checksum. - /// - internal SourceHashAlgorithm ChecksumAlgorithm - => _textLoader.ChecksumAlgorithm; - public DocumentInfo WithId(DocumentId id) => With(attributes: Attributes.With(id: id ?? throw new ArgumentNullException(nameof(id)))); @@ -143,6 +143,9 @@ public DocumentInfo WithFilePath(string? filePath) public DocumentInfo WithTextLoader(TextLoader? loader) => With(loader: loader ?? NullTextLoader.Default); + public DocumentInfo WithLoadTextOptions(LoadTextOptions options) + => With(loadTextOptions: options); + internal DocumentInfo WithDesignTimeOnly(bool designTimeOnly) => With(attributes: Attributes.With(designTimeOnly: designTimeOnly)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index dbe07544fdea2..e5a828feea27b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -26,6 +26,7 @@ internal partial class DocumentState : TextDocumentState private static readonly ConditionalWeakTable s_syntaxTreeToIdMap = new(); + // properties inherited from the containing project: private readonly HostLanguageServices _languageServices; private readonly ParseOptions? _options; @@ -40,8 +41,9 @@ protected DocumentState( ParseOptions? options, SourceText? sourceText, ITextAndVersionSource textSource, + LoadTextOptions loadTextOptions, ValueSource? treeSource) - : base(solutionServices, documentServiceProvider, attributes, sourceText, textSource) + : base(solutionServices, documentServiceProvider, attributes, sourceText, textSource, loadTextOptions) { Contract.ThrowIfFalse(_options is null == _treeSource is null); @@ -71,7 +73,8 @@ public DocumentState( { Contract.ThrowIfNull(options); _treeSource = CreateLazyFullyParsedTree( - base.TextAndVersionSource, + TextAndVersionSource, + LoadTextOptions, info.Id.ProjectId, GetSyntaxTreeFilePath(info.Attributes), options, @@ -112,6 +115,7 @@ private static string GetSyntaxTreeFilePath(DocumentInfo.DocumentAttributes info protected static ValueSource CreateLazyFullyParsedTree( ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -119,13 +123,14 @@ protected static ValueSource CreateLazyFullyParsedTree( PreservationMode mode = PreservationMode.PreserveValue) { return new AsyncLazy( - c => FullyParseTreeAsync(newTextSource, cacheKey, filePath, options, languageServices, mode, c), - c => FullyParseTree(newTextSource, cacheKey, filePath, options, languageServices, mode, c), + c => FullyParseTreeAsync(newTextSource, loadTextOptions, cacheKey, filePath, options, languageServices, mode, c), + c => FullyParseTree(newTextSource, loadTextOptions, cacheKey, filePath, options, languageServices, mode, c), cacheResult: true); } private static async Task FullyParseTreeAsync( ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -135,7 +140,7 @@ private static async Task FullyParseTreeAsync( { using (Logger.LogBlock(FunctionId.Workspace_Document_State_FullyParseSyntaxTree, s_fullParseLog, filePath, mode, cancellationToken)) { - var textAndVersion = await newTextSource.GetValueAsync(cancellationToken).ConfigureAwait(false); + var textAndVersion = await newTextSource.GetValueAsync(loadTextOptions, cancellationToken).ConfigureAwait(false); var treeAndVersion = CreateTreeAndVersion(newTextSource, cacheKey, filePath, options, languageServices, mode, textAndVersion, cancellationToken); // The tree may be a RecoverableSyntaxTree. In its initial state, the RecoverableSyntaxTree keeps a @@ -152,6 +157,7 @@ private static async Task FullyParseTreeAsync( private static TreeAndVersion FullyParseTree( ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -161,7 +167,7 @@ private static TreeAndVersion FullyParseTree( { using (Logger.LogBlock(FunctionId.Workspace_Document_State_FullyParseSyntaxTree, s_fullParseLog, filePath, mode, cancellationToken)) { - var textAndVersion = newTextSource.GetValue(cancellationToken); + var textAndVersion = newTextSource.GetValue(loadTextOptions, cancellationToken); var treeAndVersion = CreateTreeAndVersion(newTextSource, cacheKey, filePath, options, languageServices, mode, textAndVersion, cancellationToken); // The tree may be a RecoverableSyntaxTree. In its initial state, the RecoverableSyntaxTree keeps a @@ -207,24 +213,26 @@ private static TreeAndVersion CreateTreeAndVersion( private static ValueSource CreateLazyIncrementallyParsedTree( ValueSource oldTreeSource, - ITextAndVersionSource newTextSource) + ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions) { return new AsyncLazy( - c => IncrementallyParseTreeAsync(oldTreeSource, newTextSource, c), - c => IncrementallyParseTree(oldTreeSource, newTextSource, c), + c => IncrementallyParseTreeAsync(oldTreeSource, newTextSource, loadTextOptions, c), + c => IncrementallyParseTree(oldTreeSource, newTextSource, loadTextOptions, c), cacheResult: true); } private static async Task IncrementallyParseTreeAsync( ValueSource oldTreeSource, ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, CancellationToken cancellationToken) { try { using (Logger.LogBlock(FunctionId.Workspace_Document_State_IncrementallyParseSyntaxTree, cancellationToken)) { - var newTextAndVersion = await newTextSource.GetValueAsync(cancellationToken).ConfigureAwait(false); + var newTextAndVersion = await newTextSource.GetValueAsync(loadTextOptions, cancellationToken).ConfigureAwait(false); var oldTreeAndVersion = await oldTreeSource.GetValueAsync(cancellationToken).ConfigureAwait(false); return IncrementallyParse(newTextAndVersion, oldTreeAndVersion, cancellationToken); @@ -239,13 +247,14 @@ private static async Task IncrementallyParseTreeAsync( private static TreeAndVersion IncrementallyParseTree( ValueSource oldTreeSource, ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, CancellationToken cancellationToken) { try { using (Logger.LogBlock(FunctionId.Workspace_Document_State_IncrementallyParseSyntaxTree, cancellationToken)) { - var newTextAndVersion = newTextSource.GetValue(cancellationToken); + var newTextAndVersion = newTextSource.GetValue(loadTextOptions, cancellationToken); var oldTreeAndVersion = oldTreeSource.GetValue(cancellationToken); return IncrementallyParse(newTextAndVersion, oldTreeAndVersion, cancellationToken); @@ -326,6 +335,31 @@ public bool HasContentChanged(DocumentState oldState) public bool HasTextChanged(DocumentState oldState) => HasTextChanged(oldState, ignoreUnchangeableDocument: false); + public DocumentState UpdateChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + { + var newLoadTextOptions = new LoadTextOptions(checksumAlgorithm); + + // TODO: we should be able to reuse the tree + var newTreeSource = CreateLazyFullyParsedTree( + TextAndVersionSource, + newLoadTextOptions, + Id.ProjectId, + GetSyntaxTreeFilePath(Attributes), + _options, + _languageServices); + + return new DocumentState( + LanguageServices, + LanguageServices.WorkspaceServices, + Services, + Attributes, + _options, + sourceText, + TextAndVersionSource, + newLoadTextOptions, + newTreeSource); + } + public DocumentState UpdateParseOptions(ParseOptions options, bool onlyPreprocessorDirectiveChange) { var originalSourceKind = this.SourceCodeKind; @@ -381,6 +415,7 @@ private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocesso // If we weren't able to reuse in a smart way, just reparse newTreeSource ??= CreateLazyFullyParsedTree( TextAndVersionSource, + LoadTextOptions, Id.ProjectId, GetSyntaxTreeFilePath(Attributes), options, @@ -394,6 +429,7 @@ private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocesso options, sourceText, TextAndVersionSource, + LoadTextOptions, newTreeSource); } @@ -427,6 +463,7 @@ private DocumentState UpdateAttributes(DocumentInfo.DocumentAttributes attribute _options, sourceText, TextAndVersionSource, + LoadTextOptions, _treeSource); } @@ -440,6 +477,7 @@ public DocumentState UpdateFilePath(string? filePath) var newTreeSource = SupportsSyntaxTree ? CreateLazyFullyParsedTree( TextAndVersionSource, + LoadTextOptions, Id.ProjectId, GetSyntaxTreeFilePath(newAttributes), _options!, @@ -453,22 +491,10 @@ public DocumentState UpdateFilePath(string? filePath) _options, sourceText, TextAndVersionSource, + LoadTextOptions, newTreeSource); } - public DocumentState UpdateChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) - { - var newTextSource = TextAndVersionSource.TryUpdateChecksumAlgorithm(checksumAlgorithm); - if (newTextSource == null || newTextSource == TextAndVersionSource) - { - return this; - } - - // Changing the checksum algorithm does not change the tree nodes but the tree itself needs an update. - // Incremental update should be able to reuse the entire root node. - return (DocumentState)UpdateText(newTextSource, PreservationMode.PreserveValue, incremental: true); - } - public new DocumentState UpdateText(SourceText newText, PreservationMode mode) => (DocumentState)base.UpdateText(newText, mode); @@ -485,12 +511,13 @@ protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSou } else if (incremental) { - newTreeSource = CreateLazyIncrementallyParsedTree(_treeSource, newTextSource); + newTreeSource = CreateLazyIncrementallyParsedTree(_treeSource, newTextSource, LoadTextOptions); } else { newTreeSource = CreateLazyFullyParsedTree( newTextSource, + LoadTextOptions, Id.ProjectId, GetSyntaxTreeFilePath(Attributes), _options!, @@ -506,6 +533,7 @@ protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSou _options, sourceText: null, textSource: newTextSource, + LoadTextOptions, treeSource: newTreeSource); } @@ -522,8 +550,6 @@ internal DocumentState UpdateText(TextLoader loader, SourceText? text, Preservat return documentState; } - Debug.Assert(text.ChecksumAlgorithm == loader.ChecksumAlgorithm); - return new DocumentState( LanguageServices, solutionServices, @@ -532,6 +558,7 @@ internal DocumentState UpdateText(TextLoader loader, SourceText? text, Preservat _options, sourceText: text, textSource: documentState.TextAndVersionSource, + LoadTextOptions, treeSource: documentState._treeSource); } @@ -568,8 +595,8 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) var filePath = GetSyntaxTreeFilePath(Attributes); Contract.ThrowIfNull(_options); - var (text, treeAndVersion) = CreateRecoverableTextAndTree(newRoot, filePath, newTextVersion, newTreeVersion, encoding, ChecksumAlgorithm, Attributes, _options, syntaxTreeFactory, mode); - Debug.Assert(treeAndVersion.ChecksumAlgorithm == ChecksumAlgorithm); + var (text, treeAndVersion) = CreateRecoverableTextAndTree(newRoot, filePath, newTextVersion, newTreeVersion, encoding, LoadTextOptions.ChecksumAlgorithm, Attributes, _options, syntaxTreeFactory, mode); + Debug.Assert(treeAndVersion.ChecksumAlgorithm == LoadTextOptions.ChecksumAlgorithm); return new DocumentState( LanguageServices, @@ -579,6 +606,7 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) _options, sourceText: null, textSource: text, + LoadTextOptions, treeSource: new ConstantValueSource(treeAndVersion)); } @@ -667,7 +695,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT private VersionStamp GetNewerVersion() { - if (TextAndVersionSource.TryGetValue(out var textAndVersion)) + if (TextAndVersionSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { return textAndVersion!.Version.GetNewerVersion(); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs index 0153e9da0a61c..a2f12caaf7f0d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs @@ -15,7 +15,7 @@ internal partial class DocumentState /// /// A source for constructed from an syntax tree. /// - private sealed class TreeTextSource : ValueSource, ITextAndVersionSource, ITextVersionable + private sealed class TreeTextSource : ITextAndVersionSource, ITextVersionable { private readonly ValueSource _textSource; private readonly VersionStamp _version; @@ -31,19 +31,19 @@ public TreeTextSource(ValueSource textSource, VersionStamp version, ChecksumAlgorithm = checksumAlgorithm; } - public override async Task GetValueAsync(CancellationToken cancellationToken = default) + public async Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) { var text = await _textSource.GetValueAsync(cancellationToken).ConfigureAwait(false); return TextAndVersion.Create(text, _version, _filePath); } - public override TextAndVersion GetValue(CancellationToken cancellationToken = default) + public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) { var text = _textSource.GetValue(cancellationToken); return TextAndVersion.Create(text, _version, _filePath); } - public override bool TryGetValue([NotNullWhen(true)] out TextAndVersion? value) + public bool TryGetValue(LoadTextOptions options, [NotNullWhen(true)] out TextAndVersion? value) { if (_textSource.TryGetValue(out var text)) { @@ -57,14 +57,11 @@ public override bool TryGetValue([NotNullWhen(true)] out TextAndVersion? value) } } - public bool TryGetTextVersion(out VersionStamp version) + public bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version) { version = _version; return version != default; } - - public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) - => null; } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs index 43d9f09a1154c..76f0c709f2f43 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs @@ -31,11 +31,6 @@ public class FileTextLoader : TextLoader /// public Encoding? DefaultEncoding { get; } - /// - /// Algorithm used to calculate content checksum. - /// - internal override SourceHashAlgorithm ChecksumAlgorithm { get; } - /// /// Creates a content loader for specified file. /// @@ -48,35 +43,13 @@ public class FileTextLoader : TextLoader /// is null. /// is not an absolute path. public FileTextLoader(string path, Encoding? defaultEncoding) - : this(path, defaultEncoding, SourceHashAlgorithm.Sha1) - { - } - - /// - /// Creates a content loader for specified file. - /// - /// An absolute file path. - /// - /// Specifies an encoding to be used if the actual encoding can't be determined from the stream content (the stream doesn't start with Byte Order Mark). - /// If not specified auto-detect heuristics are used to determine the encoding. - /// Note that if the stream starts with Byte Order Mark the value of is ignored. - /// - /// Algorithm used to calculate content checksum. - /// is null. - /// is not an absolute path. - internal FileTextLoader(string path, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm) { CompilerPathUtilities.RequireAbsolutePath(path, "path"); - Contract.ThrowIfFalse(SourceHashAlgorithms.IsSupportedAlgorithm(checksumAlgorithm)); Path = path; DefaultEncoding = defaultEncoding; - ChecksumAlgorithm = checksumAlgorithm; } - private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) - => new FileTextLoader(Path, DefaultEncoding, algorithm); - /// /// We have this limit on file size to reduce a chance of OOM when user adds massive files to the solution (often by accident). /// The threshold is 100MB which came from some internal data on big files and some discussion. @@ -97,7 +70,7 @@ protected virtual SourceText CreateText(Stream stream, Workspace? workspace) /// /// Creates from . /// - protected virtual SourceText CreateText(Stream stream, CancellationToken cancellationToken) + protected virtual SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) #pragma warning disable CS0618 // Type or member is obsolete => CreateText(stream, workspace: null); #pragma warning restore @@ -107,7 +80,7 @@ protected virtual SourceText CreateText(Stream stream, CancellationToken cancell /// /// /// - public override async Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -188,7 +161,7 @@ public override async Task LoadTextAndVersionAsync(CancellationT // we do this so that we asynchronously read from file. and this should allocate less for IDE case. // but probably not for command line case where it doesn't use more sophisticated services. using var readStream = await SerializableBytes.CreateReadableStreamAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); - var text = CreateText(readStream, cancellationToken); + var text = CreateText(readStream, options, cancellationToken); textAndVersion = TextAndVersion.Create(text, version, Path); } @@ -211,7 +184,7 @@ public override async Task LoadTextAndVersionAsync(CancellationT /// /// /// - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -223,7 +196,7 @@ internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationTok using (var stream = FileUtilities.RethrowExceptionsAsIOException(() => new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, bufferSize: 4096, useAsync: false))) { var version = VersionStamp.Create(prevLastWriteTime); - var text = CreateText(stream, cancellationToken); + var text = CreateText(stream, options, cancellationToken); textAndVersion = TextAndVersion.Create(text, version, Path); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs index 2d1e4731aa7d9..8fca535535e05 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs @@ -11,10 +11,7 @@ namespace Microsoft.CodeAnalysis; internal interface ITextAndVersionSource { - bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value); - TextAndVersion GetValue(CancellationToken cancellationToken); - Task GetValueAsync(CancellationToken cancellationToken); - - SourceHashAlgorithm ChecksumAlgorithm { get; } - ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm); + bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value); + TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken); + Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ITextVersionable.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ITextVersionable.cs index 238c56a1e393a..3d8f25fca28e6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ITextVersionable.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ITextVersionable.cs @@ -8,6 +8,6 @@ namespace Microsoft.CodeAnalysis { internal interface ITextVersionable { - bool TryGetTextVersion(out VersionStamp version); + bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs index 005ff34ba7bad..02eec8be2c010 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs @@ -11,51 +11,64 @@ namespace Microsoft.CodeAnalysis; -internal sealed class LoadableTextAndVersionSource : ValueSource, ITextAndVersionSource +internal sealed class LoadableTextAndVersionSource : ITextAndVersionSource { - private readonly AsyncLazy _lazy; - private readonly TextLoader _loader; - - public LoadableTextAndVersionSource(TextLoader loader, bool cacheResult) + private sealed class LazyValueWithOptions { - _loader = loader; - _lazy = new AsyncLazy(LoadAsync, LoadSynchronously, cacheResult); - } + public readonly LoadableTextAndVersionSource Source; + public readonly AsyncLazy LazyValue; + public readonly LoadTextOptions Options; - private Task LoadAsync(CancellationToken cancellationToken) - => _loader.LoadTextAsync(cancellationToken); + public LazyValueWithOptions(LoadableTextAndVersionSource source, LoadTextOptions options) + { + LazyValue = new AsyncLazy(LoadAsync, LoadSynchronously, source.CacheResult); + Source = source; + Options = options; + } - private TextAndVersion LoadSynchronously(CancellationToken cancellationToken) - => _loader.LoadTextSynchronously(cancellationToken); + private Task LoadAsync(CancellationToken cancellationToken) + => Source.Loader.LoadTextAsync(Options, cancellationToken); - public override TextAndVersion GetValue(CancellationToken cancellationToken) - => _lazy.GetValue(cancellationToken); + private TextAndVersion LoadSynchronously(CancellationToken cancellationToken) + => Source.Loader.LoadTextSynchronously(Options, cancellationToken); + } - public override bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value) - => _lazy.TryGetValue(out value); + public readonly TextLoader Loader; + public readonly bool CacheResult; - public override Task GetValueAsync(CancellationToken cancellationToken) - => _lazy.GetValueAsync(cancellationToken); + private LazyValueWithOptions? _lazyValue; - public TextLoader Loader - => _loader; + public LoadableTextAndVersionSource(TextLoader loader, bool cacheResult) + { + Loader = loader; + CacheResult = cacheResult; + } - public SourceHashAlgorithm ChecksumAlgorithm - => _loader.ChecksumAlgorithm; + /// + /// True if the text content can be reloaded from the underlying binary representation (e.g. on disk). + /// + public bool IsReloadable + => Loader is FileTextLoader; - public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) + private AsyncLazy GetLazyValue(LoadTextOptions options) { - var newLoader = _loader.TryUpdateChecksumAlgorithm(algorithm); - if (newLoader == null) - { - return null; - } + var lazy = _lazyValue; - if (newLoader == _loader) + if (lazy == null || lazy.Options != options) { - return this; + // drop previous value and replace it with the one that has current options: + _lazyValue = lazy = new LazyValueWithOptions(this, options); } - return new LoadableTextAndVersionSource(newLoader, _lazy.CacheResult); + return lazy.LazyValue; } + + public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) + => GetLazyValue(options).GetValue(cancellationToken); + + public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) + => GetLazyValue(options).TryGetValue(out value); + + public Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) + => GetLazyValue(options).GetValueAsync(cancellationToken); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs index f5b7668f8c5e1..93bc6cd2b2997 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs @@ -13,22 +13,15 @@ namespace Microsoft.CodeAnalysis; /// /// that does not load text. -/// It only carries . /// internal sealed class NullTextLoader : TextLoader { - public static readonly NullTextLoader Default = new(SourceHashAlgorithm.Sha1); + public static readonly NullTextLoader Default = new(); - internal override SourceHashAlgorithm ChecksumAlgorithm { get; } - - public NullTextLoader(SourceHashAlgorithm checksumAlgorithm) + public NullTextLoader() { - ChecksumAlgorithm = checksumAlgorithm; } - private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) - => new NullTextLoader(algorithm); - - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => throw new NotImplementedException(); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 82b5029d78cdb..1fac42a205ed0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -756,9 +756,6 @@ public ProjectState WithChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) documentStates: UpdateDocumentsChecksumAlgorithm(checksumAlgorithm)); } - /// - /// Update checksum algorithm of all documents backed by a text loader that supports reloading the content and calculating a new checksum (e.g. file loader). - /// private TextDocumentStates UpdateDocumentsChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) => DocumentStates.UpdateStates(static (state, checksumAlgorithm) => state.UpdateChecksumAlgorithm(checksumAlgorithm), checksumAlgorithm); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs index ded793218e68b..1d09386492ebb 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis /// /// A recoverable TextAndVersion source that saves its text to temporary storage. /// - internal sealed class RecoverableTextAndVersion : ValueSource, ITextVersionable, ITextAndVersionSource + internal sealed class RecoverableTextAndVersion : ITextVersionable, ITextAndVersionSource { private readonly SolutionServices _services; @@ -49,14 +49,14 @@ private bool TryGetInitialSourceOrRecoverableText([NotNullWhen(true)] out ITextA public ITemporaryTextStorageInternal? Storage => (_initialSourceOrRecoverableText as RecoverableText)?.Storage; - public override bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value) + public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) { if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) { - return source.TryGetValue(out value); + return source.TryGetValue(options, out value); } - if (recoverableText.TryGetValue(out var text)) + if (recoverableText.TryGetValue(out var text) && recoverableText.LoadTextOptions == options) { value = TextAndVersion.Create(text, recoverableText.Version, recoverableText.LoadDiagnostic); return true; @@ -66,14 +66,14 @@ public override bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value return false; } - public bool TryGetTextVersion(out VersionStamp version) + public bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version) { if (_initialSourceOrRecoverableText is ITextVersionable textVersionable) { - return textVersionable.TryGetTextVersion(out version); + return textVersionable.TryGetTextVersion(options, out version); } - if (TryGetValue(out var textAndVersion)) + if (TryGetValue(options, out var textAndVersion)) { version = textAndVersion.Version; return true; @@ -83,60 +83,54 @@ public bool TryGetTextVersion(out VersionStamp version) return false; } - public override TextAndVersion GetValue(CancellationToken cancellationToken = default) + public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) { if (_initialSourceOrRecoverableText is ITextAndVersionSource source) { // replace initial source with recovarable text if it hasn't been replaced already: Interlocked.CompareExchange( ref _initialSourceOrRecoverableText, - value: new RecoverableText(source, source.GetValue(cancellationToken), _services), + value: new RecoverableText(source, source.GetValue(options, cancellationToken), options, _services), comparand: source); } + // If we have a recoverable text but the options it was created for do not match the current options + // and the initial source supports reloading, reload and replace the recoverable text. var recoverableText = (RecoverableText)_initialSourceOrRecoverableText; + if (recoverableText.LoadTextOptions != options && recoverableText.InitialSource != null) + { + Interlocked.Exchange( + ref _initialSourceOrRecoverableText, + new RecoverableText(recoverableText.InitialSource, recoverableText.InitialSource.GetValue(options, cancellationToken), options, _services)); + } + + recoverableText = (RecoverableText)_initialSourceOrRecoverableText; return recoverableText.ToTextAndVersion(recoverableText.GetValue(cancellationToken)); } - public override async Task GetValueAsync(CancellationToken cancellationToken = default) + public async Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) { if (_initialSourceOrRecoverableText is ITextAndVersionSource source) { // replace initial source with recovarable text if it hasn't been replaced already: Interlocked.CompareExchange( ref _initialSourceOrRecoverableText, - value: new RecoverableText(source, await source.GetValueAsync(cancellationToken).ConfigureAwait(false), _services), + value: new RecoverableText(source, await source.GetValueAsync(options, cancellationToken).ConfigureAwait(false), options, _services), comparand: source); } + // If we have a recoverable text but the options it was created for do not match the current options + // and the initial source supports reloading, reload and replace the recoverable text. var recoverableText = (RecoverableText)_initialSourceOrRecoverableText; - return recoverableText.ToTextAndVersion(await recoverableText.GetValueAsync(cancellationToken).ConfigureAwait(false)); - } - - public SourceHashAlgorithm ChecksumAlgorithm - => TryGetInitialSourceOrRecoverableText(out var source, out var text) ? source.ChecksumAlgorithm : text.ChecksumAlgorithm; - - public ITextAndVersionSource? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) - { - if (ChecksumAlgorithm == algorithm) + if (recoverableText.LoadTextOptions != options && recoverableText.InitialSource != null) { - return this; - } - - // store to local to avoid race: - if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) - { - var newSource = source.TryUpdateChecksumAlgorithm(algorithm); - return (newSource != null) ? new RecoverableTextAndVersion(newSource, _services) : null; - } - - var newFileLoader = recoverableText.FileLoader?.TryUpdateChecksumAlgorithm(algorithm); - if (newFileLoader == null) - { - return null; + Interlocked.Exchange( + ref _initialSourceOrRecoverableText, + new RecoverableText(recoverableText.InitialSource, await recoverableText.InitialSource.GetValueAsync(options, cancellationToken).ConfigureAwait(false), options, _services)); } - return new RecoverableTextAndVersion(new LoadableTextAndVersionSource(newFileLoader, cacheResult: false), _services); + recoverableText = (RecoverableText)_initialSourceOrRecoverableText; + return recoverableText.ToTextAndVersion(await recoverableText.GetValueAsync(cancellationToken).ConfigureAwait(false)); } private sealed class RecoverableText : WeaklyCachedRecoverableValueSource, ITextVersionable @@ -144,24 +138,26 @@ private sealed class RecoverableText : WeaklyCachedRecoverableValueSource(textAndVersion.Text)) { _storageService = services.GetRequiredService(); Version = textAndVersion.Version; LoadDiagnostic = textAndVersion.LoadDiagnostic; - ChecksumAlgorithm = source.ChecksumAlgorithm; + LoadTextOptions = options; - // preserve file loader so that we can update checksum algorithm later if necessary - if (source is LoadableTextAndVersionSource { Loader: FileTextLoader fileLoader }) + if (source is LoadableTextAndVersionSource { IsReloadable: true } reloadableSource) { - FileLoader = fileLoader; + // reloadable source must not cache results + Contract.ThrowIfTrue(reloadableSource.CacheResult); + + InitialSource = source; } } @@ -201,10 +197,10 @@ protected override async Task SaveAsync(SourceText text, CancellationToken cance Interlocked.CompareExchange(ref _storage, storage, null); } - public bool TryGetTextVersion(out VersionStamp version) + public bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version) { version = Version; - return true; + return options == LoadTextOptions; } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs index 8b092d169834e..efb66be69d4fc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs @@ -940,7 +940,7 @@ private async Task FinalizeCompilationAsync( identity, generatedSource.SourceText, generatedSource.SyntaxTree.Options, - this.ProjectState.LanguageServices, + ProjectState.LanguageServices, solution.Services)); // The count of trees was the same, but something didn't match up. Since we're here, at least one tree diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index 6b77f54d88861..576489be532da 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs @@ -21,10 +21,12 @@ public static SourceGeneratedDocumentState Create( HostLanguageServices languageServices, HostWorkspaceServices solutionServices) { + var loadTextOptions = new LoadTextOptions(generatedSourceText.ChecksumAlgorithm); var textAndVersion = TextAndVersion.Create(generatedSourceText, VersionStamp.Create()); var textSource = new ConstantTextAndVersionSource(textAndVersion); var treeSource = CreateLazyFullyParsedTree( textSource, + loadTextOptions, documentIdentity.DocumentId.ProjectId, documentIdentity.FilePath, parseOptions, @@ -45,6 +47,7 @@ public static SourceGeneratedDocumentState Create( designTimeOnly: false), parseOptions, textSource, + loadTextOptions, treeSource); } @@ -56,8 +59,9 @@ private SourceGeneratedDocumentState( DocumentInfo.DocumentAttributes attributes, ParseOptions options, ITextAndVersionSource textSource, + LoadTextOptions loadTextOptions, ValueSource treeSource) - : base(languageServices, solutionServices, documentServiceProvider, attributes, options, sourceText: null, textSource, treeSource) + : base(languageServices, solutionServices, documentServiceProvider, attributes, options, sourceText: null, textSource, loadTextOptions, treeSource) { Identity = documentIdentity; } @@ -81,6 +85,7 @@ public SourceGeneratedDocumentState WithUpdatedGeneratedContent(SourceText sourc return Create( Identity, sourceText, + LoadTextOptions, parseOptions, this.LanguageServices, this.solutionServices); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index 9ea3dd33dc898..c34e54ca3af4e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -35,6 +35,7 @@ internal partial class TextDocumentState /// protected readonly SourceText? sourceText; protected ITextAndVersionSource TextAndVersionSource { get; } + public readonly LoadTextOptions LoadTextOptions; // Checksums for this solution state private readonly ValueSource _lazyChecksums; @@ -51,11 +52,13 @@ protected TextDocumentState( IDocumentServiceProvider? documentServiceProvider, DocumentInfo.DocumentAttributes attributes, SourceText? sourceText, - ITextAndVersionSource textAndVersionSource) + ITextAndVersionSource textAndVersionSource, + LoadTextOptions loadTextOptions) { this.solutionServices = solutionServices; - this.sourceText = sourceText; + this.sourceText = sourceText; + this.LoadTextOptions = loadTextOptions; TextAndVersionSource = textAndVersionSource; Attributes = attributes; @@ -76,11 +79,11 @@ public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) sourceText: null, textAndVersionSource: info.TextLoader != null ? CreateRecoverableText(info.TextLoader, services.SolutionServices) - : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, encoding: null, info.ChecksumAlgorithm), VersionStamp.Default, info.FilePath))) + : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, encoding: null, info.LoadTextOptions.ChecksumAlgorithm), VersionStamp.Default, info.FilePath)), + info.LoadTextOptions) { } - public SourceHashAlgorithm ChecksumAlgorithm => TextAndVersionSource.ChecksumAlgorithm; public DocumentId Id => Attributes.Id; public string? FilePath => Attributes.FilePath; public IReadOnlyList Folders => Attributes.Folders; @@ -92,7 +95,7 @@ private static ITextAndVersionSource CreateStrongText(TextAndVersion text) private static ITextAndVersionSource CreateStrongText(TextLoader loader) => new LoadableTextAndVersionSource(loader, cacheResult: true); - private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, SolutionServices services) + private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, LoadTextOptions loadTextOptions, SolutionServices services) { var result = new RecoverableTextAndVersion(new ConstantTextAndVersionSource(text), services); @@ -102,7 +105,7 @@ private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, // GetValueAsync) is called. Since we know we are creating a RecoverableTextAndVersion for the purpose of // avoiding problematic address space overhead, we call GetValue immediately to force the object to weakly // hold its data from the start. - result.GetValue(); + result.GetValue(loadTextOptions, CancellationToken.None); return result; } @@ -121,7 +124,7 @@ public bool TryGetText([NotNullWhen(returnValue: true)] out SourceText? text) return true; } - if (this.TextAndVersionSource.TryGetValue(out var textAndVersion)) + if (this.TextAndVersionSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { text = textAndVersion.Text; return true; @@ -138,10 +141,10 @@ public bool TryGetTextVersion(out VersionStamp version) // try fast path first if (this.TextAndVersionSource is ITextVersionable versionable) { - return versionable.TryGetTextVersion(out version); + return versionable.TryGetTextVersion(LoadTextOptions, out version); } - if (this.TextAndVersionSource.TryGetValue(out var textAndVersion)) + if (this.TextAndVersionSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { version = textAndVersion.Version; return true; @@ -154,7 +157,7 @@ public bool TryGetTextVersion(out VersionStamp version) } public bool TryGetTextAndVersion([NotNullWhen(true)] out TextAndVersion? textAndVersion) - => TextAndVersionSource.TryGetValue(out textAndVersion); + => TextAndVersionSource.TryGetValue(LoadTextOptions, out textAndVersion); public ValueTask GetTextAsync(CancellationToken cancellationToken) { @@ -177,13 +180,13 @@ public ValueTask GetTextAsync(CancellationToken cancellationToken) public SourceText GetTextSynchronously(CancellationToken cancellationToken) { - var textAndVersion = this.TextAndVersionSource.GetValue(cancellationToken); + var textAndVersion = this.TextAndVersionSource.GetValue(LoadTextOptions, cancellationToken); return textAndVersion.Text; } public VersionStamp GetTextVersionSynchronously(CancellationToken cancellationToken) { - var textAndVersion = this.TextAndVersionSource.GetValue(cancellationToken); + var textAndVersion = this.TextAndVersionSource.GetValue(LoadTextOptions, cancellationToken); return textAndVersion.Version; } @@ -203,7 +206,7 @@ public TextDocumentState UpdateText(TextAndVersion newTextAndVersion, Preservati { var newTextSource = mode == PreservationMode.PreserveIdentity ? CreateStrongText(newTextAndVersion) - : CreateRecoverableText(newTextAndVersion, solutionServices.SolutionServices); + : CreateRecoverableText(newTextAndVersion, LoadTextOptions, solutionServices.SolutionServices); return UpdateText(newTextSource, mode, incremental: true); } @@ -233,18 +236,19 @@ protected virtual TextDocumentState UpdateText(ITextAndVersionSource newTextSour this.Services, this.Attributes, sourceText: null, - textAndVersionSource: newTextSource); + textAndVersionSource: newTextSource, + LoadTextOptions); } private ValueTask GetTextAndVersionAsync(CancellationToken cancellationToken) { - if (this.TextAndVersionSource.TryGetValue(out var textAndVersion)) + if (this.TextAndVersionSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { return new ValueTask(textAndVersion); } else { - return new ValueTask(TextAndVersionSource.GetValueAsync(cancellationToken)); + return new ValueTask(TextAndVersionSource.GetValueAsync(LoadTextOptions, cancellationToken)); } } @@ -253,7 +257,7 @@ private ValueTask GetTextAndVersionAsync(CancellationToken cance private VersionStamp GetNewerVersion() { - if (this.TextAndVersionSource.TryGetValue(out var textAndVersion)) + if (this.TextAndVersionSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { return textAndVersion.Version.GetNewerVersion(); } @@ -263,7 +267,7 @@ private VersionStamp GetNewerVersion() public virtual async Task GetTopLevelChangeTextVersionAsync(CancellationToken cancellationToken) { - var textAndVersion = await this.TextAndVersionSource.GetValueAsync(cancellationToken).ConfigureAwait(false); + var textAndVersion = await this.TextAndVersionSource.GetValueAsync(LoadTextOptions, cancellationToken).ConfigureAwait(false); return textAndVersion.Version; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index 6c8d4d8f98052..5cf0b67604220 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Drawing; using System.IO; using System.Reflection; using System.Text; @@ -15,6 +16,8 @@ namespace Microsoft.CodeAnalysis { + public readonly record struct LoadTextOptions(SourceHashAlgorithm ChecksumAlgorithm); + /// /// A class that represents access to a source text and its version from a storage location. /// @@ -28,27 +31,24 @@ public abstract class TextLoader internal virtual string? FilePath => null; - internal virtual SourceHashAlgorithm ChecksumAlgorithm => SourceHashAlgorithm.Sha1; - - internal TextLoader? TryUpdateChecksumAlgorithm(SourceHashAlgorithm algorithm) - => (ChecksumAlgorithm == algorithm) ? this : TryUpdateChecksumAlgorithmImpl(algorithm); - - private protected virtual TextLoader? TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) - => null; - /// /// Load a text and a version of the document. /// + /// + /// Use when creating from a stream. + /// Ignore if the is not created from a binary stream. + /// + /// Cancellation token. /// /// /// - public virtual Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public virtual Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { #pragma warning disable CS0618 // Type or member is obsolete if (ImmutableInterlocked.GetOrAdd( ref s_isObsoleteLoadTextAndVersionAsyncOverriden, GetType(), - new Func>(LoadTextAndVersionAsync).Method.DeclaringType != typeof(TextLoader))) + _ => new Func>(LoadTextAndVersionAsync).Method.DeclaringType != typeof(TextLoader))) { return LoadTextAndVersionAsync(workspace: null, documentId: null, cancellationToken); } @@ -75,13 +75,13 @@ public virtual Task LoadTextAndVersionAsync(Workspace? workspace /// /// /// - internal virtual TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal virtual TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) { // this implementation exists in case a custom derived type does not have access to internals - return LoadTextAndVersionAsync(cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); + return LoadTextAndVersionAsync(options, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); } - internal async Task LoadTextAsync(CancellationToken cancellationToken) + internal async Task LoadTextAsync(LoadTextOptions options, CancellationToken cancellationToken) { var retries = 0; @@ -89,7 +89,7 @@ internal async Task LoadTextAsync(CancellationToken cancellation { try { - return await LoadTextAndVersionAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); + return await LoadTextAndVersionAsync(options, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } catch (IOException e) { @@ -110,7 +110,7 @@ internal async Task LoadTextAsync(CancellationToken cancellation } } - internal TextAndVersion LoadTextSynchronously(CancellationToken cancellationToken) + internal TextAndVersion LoadTextSynchronously(LoadTextOptions options, CancellationToken cancellationToken) { var retries = 0; @@ -118,7 +118,7 @@ internal TextAndVersion LoadTextSynchronously(CancellationToken cancellationToke { try { - return LoadTextAndVersionSynchronously(cancellationToken); + return LoadTextAndVersionSynchronously(options, cancellationToken); } catch (IOException e) { @@ -201,13 +201,10 @@ private sealed class TextDocumentLoader : TextLoader internal TextDocumentLoader(TextAndVersion textAndVersion) => _textAndVersion = textAndVersion; - internal override SourceHashAlgorithm ChecksumAlgorithm - => _textAndVersion.Text.ChecksumAlgorithm; - - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(_textAndVersion); - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) => _textAndVersion; } @@ -227,10 +224,10 @@ internal TextContainerLoader(SourceTextContainer container, VersionStamp version internal override string? FilePath => _filePath; - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(options, cancellationToken)); - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) => TextAndVersion.Create(_container.CurrentText, _version, _filePath); } } diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs index 4133578f826d6..b3e2c29dff10a 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs @@ -19,13 +19,13 @@ internal class WorkspaceFileTextLoader : FileTextLoader { private readonly ITextFactoryService _textFactory; - internal WorkspaceFileTextLoader(SolutionServices services, string path, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm) - : base(path, defaultEncoding, checksumAlgorithm) + internal WorkspaceFileTextLoader(SolutionServices services, string path, Encoding? defaultEncoding) + : base(path, defaultEncoding) { _textFactory = services.GetRequiredService(); } - protected override SourceText CreateText(Stream stream, CancellationToken cancellationToken) - => _textFactory.CreateText(stream, DefaultEncoding, ChecksumAlgorithm, cancellationToken); + protected override SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) + => _textFactory.CreateText(stream, DefaultEncoding, options.ChecksumAlgorithm, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 65024c1af3ab7..1af118d0cd740 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -474,13 +474,10 @@ public ReuseVersionLoader(DocumentState oldDocumentState, SourceText newText) _newText = newText; } - internal override SourceHashAlgorithm ChecksumAlgorithm - => _newText.ChecksumAlgorithm; - internal override string? FilePath => _oldDocumentState.FilePath; - public override async Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { var oldText = await _oldDocumentState.GetTextAsync(cancellationToken).ConfigureAwait(false); var version = await _oldDocumentState.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); @@ -488,7 +485,7 @@ public override async Task LoadTextAndVersionAsync(CancellationT return GetProperTextAndVersion(oldText, _newText, version, _oldDocumentState.FilePath); } - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) { var oldText = _oldDocumentState.GetTextSynchronously(cancellationToken); var version = _oldDocumentState.GetTextVersionSynchronously(cancellationToken); diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 4903be21b379b..d621c53bf6867 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -2223,7 +2223,7 @@ public void TestDocumentChangedOnDiskIsNotObserved() var did = DocumentId.CreateNewId(pid); sol = sol.AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8, SourceHashAlgorithms.Default)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); var observedText = GetObservedText(sol, did, text1); @@ -2282,7 +2282,7 @@ public void TestGetLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8, SourceHashAlgorithms.Default)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); var doc = sol.GetDocument(did); @@ -2349,7 +2349,7 @@ public void TestGetSyntaxTreeFromLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8, SourceHashAlgorithms.Default)); + .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); var doc = sol.GetDocument(did); var docTree = doc.GetSyntaxTreeAsync().Result; @@ -2868,7 +2868,7 @@ public async Task TestDocumentFileAccessFailureMissingFile() var did = DocumentId.CreateNewId(pid); solution = solution.AddProject(pid, "goo", "goo", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(solution.Services, @"C:\doesnotexist.cs", Encoding.UTF8, SourceHashAlgorithms.Default)) + .AddDocument(did, "x", new WorkspaceFileTextLoader(solution.Services, @"C:\doesnotexist.cs", Encoding.UTF8)) .WithDocumentFilePath(did, "document path"); var doc = solution.GetDocument(did); diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb index c0814d64644f3..0ca52560d6b47 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb @@ -86,16 +86,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - Public Overrides Function TryGetText(ByRef text As SourceText) As Boolean - Return _info.TryGetText(text) + Public Overrides Function TryGetText(options As LoadTextOptions, ByRef text As SourceText) As Boolean + Return _info.TryGetText(options, text) End Function - Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText - Return _info.TextSource.GetValue(cancellationToken).Text + Public Overrides Function GetText(options As LoadTextOptions, cancellationToken As CancellationToken) As SourceText + Return _info.TextSource.GetValue(options, cancellationToken).Text End Function - Public Overrides Function GetTextAsync(Optional cancellationToken As CancellationToken = Nothing) As Task(Of SourceText) - Return _info.GetTextAsync(cancellationToken) + Public Overrides Function GetTextAsync(options As LoadTextOptions, cancellationToken As CancellationToken) As Task(Of SourceText) + Return _info.GetTextAsync(options, cancellationToken) End Function Public Overrides ReadOnly Property Encoding As Encoding From e7379f0605d27d3e98c705254a7ff23f2c572889 Mon Sep 17 00:00:00 2001 From: tmat Date: Sat, 24 Sep 2022 12:46:33 -0700 Subject: [PATCH 12/32] LoadTextOptions --- .../RazorLineFormattingOptionsTests.cs | 3 +- .../EditAndContinueWorkspaceServiceTests.cs | 38 ++++++++---- .../TestUtilities/Rename/RenamerTests.cs | 3 +- .../Workspaces/TestHostDocument.cs | 3 +- .../Configuration/ConfigurationUpdater.cs | 3 +- .../EditAndContinue/CommittedSolution.cs | 3 +- .../AbstractGenerateTypeService.Editor.cs | 3 +- .../MetadataAsSourceGeneratedFileInfo.cs | 11 ++-- ...rceDocumentMetadataAsSourceFileProvider.cs | 4 +- .../Workspace/MiscellaneousFileUtilities.cs | 3 +- .../Lsif/Generator/CompilerInvocation.cs | 3 +- .../RazorLanguageServerFactoryWrapper.cs | 2 +- .../Implementation/AbstractEditorFactory.cs | 3 +- .../PreviewUpdater.PreviewDialogWorkspace.cs | 9 +-- ...tudioProject.BatchingDocumentCollection.cs | 20 +++--- .../Core/Def/Venus/ContainedLanguage.cs | 3 +- .../RoslynRemoteProjectInfoProvider.cs | 7 ++- .../WorkspaceFileTextLoaderNoException.cs | 14 ++--- .../Client/RemoteLanguageServiceWorkspace.cs | 7 ++- ...reeFactoryService.RecoverableSyntaxTree.cs | 4 +- .../CSharpSyntaxTreeFactoryService.cs | 8 ++- .../CodeGeneration/SymbolEditorTests.cs | 3 +- .../MSBuild/MSBuildProjectLoader.Worker.cs | 4 +- .../Core/Portable/PublicAPI.Unshipped.txt | 6 ++ .../Core/Portable/Workspace/AdhocWorkspace.cs | 2 +- .../Portable/Workspace/CommandLineProject.cs | 8 ++- ...ryService.AbstractRecoverableSyntaxRoot.cs | 17 ++++-- .../AbstractSyntaxTreeFactoryService.cs | 2 +- .../ISyntaxTreeFactoryService.cs | 2 +- .../Solution/ConstantTextAndVersionSource.cs | 3 + .../Workspace/Solution/DocumentInfo.cs | 51 ++++++++++------ .../Workspace/Solution/DocumentState.cs | 35 +++++++---- .../Solution/DocumentState_TreeTextSource.cs | 6 +- .../Workspace/Solution/FileTextLoader.cs | 20 +++++- .../Solution/ITextAndVersionSource.cs | 5 ++ .../Solution/LoadableTextAndVersionSource.cs | 7 +-- .../Workspace/Solution/NullTextLoader.cs | 27 -------- .../Solution/RecoverableTextAndVersion.cs | 7 ++- .../Portable/Workspace/Solution/Solution.cs | 9 ++- .../Solution/SourceGeneratedDocumentState.cs | 1 - .../Portable/Workspace/Solution/TextLoader.cs | 11 +++- .../Core/Portable/Workspace/Workspace.cs | 5 +- .../SolutionTests/DocumentInfoTests.cs | 2 +- .../CoreTest/SolutionTests/SolutionTests.cs | 61 +++++++++---------- .../CoreTest/SolutionTests/TextLoaderTests.cs | 10 +-- .../CoreTestUtilities/TestTextLoader.cs | 11 ++-- .../VisualStudioMSBuildWorkspaceTests.cs | 4 +- .../Remote/Core/AbstractAssetProvider.cs | 9 +-- ...reeFactoryService.RecoverableSyntaxTree.vb | 15 +++-- .../VisualBasicSyntaxTreeFactoryService.vb | 2 + 50 files changed, 286 insertions(+), 213 deletions(-) delete mode 100644 src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs diff --git a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs index cd5f5e8c6ef8c..788e0e7ae482a 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs @@ -60,7 +60,8 @@ void F () {} folders: Array.Empty(), sourceCodeKind: SourceCodeKind.Regular, loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), "file.razor.g.cs")), - filePath: "file.razor.g.cs") + filePath: "file.razor.g.cs", + loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default)) .WithDesignTimeOnly(true) .WithDocumentServiceProvider(new TestRazorDocumentServiceProvider()); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 55da33c265383..eda7dad424a4c 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -369,7 +369,9 @@ private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, st sourceCodeKind: SourceCodeKind.Regular, loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), path)), filePath: path, - isGenerated: false).WithDesignTimeOnly(true); + isGenerated: false, + loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) + .WithDesignTimeOnly(true); } internal sealed class FailingTextLoader : TextLoader @@ -499,30 +501,34 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA), - filePath: sourceFileA.Path)); + filePath: sourceFileA.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); var documentIdB = DocumentId.CreateNewId(projectP.Id, debugName: "B"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdB, name: "B", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), - filePath: sourceFileB.Path)); + filePath: sourceFileB.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); var documentIdC = DocumentId.CreateNewId(projectP.Id, debugName: "C"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdC, name: "C", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC), - filePath: sourceFileC.Path)); + filePath: sourceFileC.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); var documentIdE = DocumentId.CreateNewId(projectP.Id, debugName: "E"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdE, name: "E", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE), - filePath: sourceFileE.Path)); + filePath: sourceFileE.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); - // check that are testing documents whose hash algorithm does not match the PDB (but the hash itself does): + // check that we are testing documents whose hash algorithm does not match the PDB (but the hash itself does): Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdA).GetTextSynchronously(default).ChecksumAlgorithm); Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdB).GetTextSynchronously(default).ChecksumAlgorithm); Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdC).GetTextSynchronously(default).ChecksumAlgorithm); @@ -541,7 +547,8 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume id: DocumentId.CreateNewId(projectQ.Id, debugName: "D"), name: "D", loader: new FailingTextLoader(), - filePath: sourceFileD.Path)); + filePath: sourceFileD.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha256))); var captureMatchingDocuments = captureAllDocuments ? ImmutableArray.Empty : @@ -720,7 +727,9 @@ public async Task DesignTimeOnlyDocument_Dynamic() name: "design-time-only.cs", loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), "design-time-only.cs")), filePath: "design-time-only.cs", - isGenerated: false).WithDesignTimeOnly(true); + isGenerated: false, + loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) + .WithDesignTimeOnly(true); solution = solution.AddDocument(documentInfo); @@ -3633,7 +3642,9 @@ public async Task ActiveStatements_ForeignDocument(bool withPath, bool designTim DocumentId.CreateNewId(project.Id, "test"), name: "test", loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), filePath)), - filePath: filePath).WithDesignTimeOnly(designTimeOnly); + filePath: filePath, + loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) + .WithDesignTimeOnly(designTimeOnly); var document = project.Solution.AddDocument(documentInfo).GetDocument(documentInfo.Id); @@ -4394,7 +4405,8 @@ public async Task MultiSession() id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), - filePath: sourceFileA.Path)); + filePath: sourceFileA.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); var tasks = Enumerable.Range(0, 10).Select(async i => { @@ -4480,7 +4492,8 @@ public async Task WatchHotReloadServiceTest() id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), - filePath: sourceFileA.Path)); + filePath: sourceFileA.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); var hotReload = new WatchHotReloadService(workspace.Services, ImmutableArray.Create("Baseline", "AddDefinitionToExistingType", "NewTypeDefinition")); @@ -4547,7 +4560,8 @@ public async Task UnitTestingHotReloadServiceTest() id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), - filePath: sourceFileA.Path)); + filePath: sourceFileA.Path, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); var hotReload = new UnitTestingHotReloadService(workspace.Services); diff --git a/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs b/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs index 7a8e3d2875a09..dabb1b73e3f54 100644 --- a/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs +++ b/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs @@ -228,7 +228,8 @@ protected async Task TestRenameMappedFile(string startText, string documentName, folders: GetDocumentFolders(s_defaultDocumentPath), loader: TextLoader.From(TextAndVersion.Create(startSourceText, VersionStamp.Create(), documentName)), filePath: s_defaultDocumentPath, - isGenerated: true) + isGenerated: true, + loadTextOptions: new LoadTextOptions(startSourceText.ChecksumAlgorithm)) .WithDocumentServiceProvider(new TestDocumentServiceProvider()); solution = solution.AddDocument(documentInfo); diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index 94d1b1213b607..c64891ac58263 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -358,7 +358,8 @@ internal void CloseTextView() public DocumentInfo ToDocumentInfo() { Contract.ThrowIfTrue(IsSourceGenerated, "We shouldn't be producing a DocumentInfo for a source generated document."); - return DocumentInfo.Create(Id, Name, Folders, SourceCodeKind, Loader, FilePath).WithDocumentServiceProvider(_documentServiceProvider); + return DocumentInfo.Create(Id, Name, Folders, SourceCodeKind, Loader, FilePath, isGenerated: false, new LoadTextOptions(ChecksumAlgorithm)) + .WithDocumentServiceProvider(_documentServiceProvider); } } } diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs index a2d1ef502c60e..1beb98cff1655 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs @@ -341,7 +341,8 @@ private async Task ConfigureAsync() var documentInfo = DocumentInfo.Create( id, name: ".editorconfig", - filePath: analyzerConfigPath); + filePath: analyzerConfigPath, + loadTextOptions: new LoadTextOptions(project.State.ChecksumAlgorithm)); var newSolution = project.Solution.AddAnalyzerConfigDocuments(ImmutableArray.Create(documentInfo)); return newSolution.GetProject(project.Id)?.GetAnalyzerConfigDocument(id); diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index 2230f13e062b6..ab245d16261c4 100644 --- a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs +++ b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs @@ -292,7 +292,8 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel folders: document.Folders, loader: TextLoader.From(TextAndVersion.Create(sourceText, sourceTextVersion, document.Name)), filePath: document.FilePath, - isGenerated: document.State.Attributes.IsGenerated) + isGenerated: document.State.Attributes.IsGenerated, + loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) .WithDesignTimeOnly(document.State.Attributes.DesignTimeOnly) .WithDocumentServiceProvider(document.State.Services)); } diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index f741339d17f8d..133512feeb8df 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -358,7 +358,8 @@ private async Task> CreateAddDocumentAndUpdateU documentId, documentName, containers, - sourceCodeKind)); + sourceCodeKind, + loadTextOptions: new LoadTextOptions(projectToBeUpdated.State.ChecksumAlgorithm))); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(documentId, root, PreservationMode.PreserveIdentity); diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index 23880c67bd950..f729a8433e3c2 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -72,14 +72,15 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work ? string.Format(@"[assembly: System.Reflection.AssemblyVersion(""{0}"")]", AssemblyIdentity.Version) : string.Format(@"", AssemblyIdentity.Version); - var assemblyInfoSourceTextContainer = SourceText.From(assemblyInfoString, Encoding, ChecksumAlgorithm).Container; + var assemblyInfoSourceText = SourceText.From(assemblyInfoString, Encoding, ChecksumAlgorithm); var assemblyInfoDocument = DocumentInfo.Create( assemblyInfoDocumentId, assemblyInfoFileName, - loader: TextLoader.From(assemblyInfoSourceTextContainer, VersionStamp.Default), + loader: TextLoader.From(assemblyInfoSourceText.Container, VersionStamp.Default), filePath: null, - isGenerated: true).WithDesignTimeOnly(true); + isGenerated: true, + loadTextOptions: new LoadTextOptions(assemblyInfoSourceText.ChecksumAlgorithm)).WithDesignTimeOnly(true); var generatedDocumentId = DocumentId.CreateNewId(projectId); var generatedDocument = DocumentInfo.Create( @@ -87,7 +88,9 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work Path.GetFileName(TemporaryFilePath), loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding) : null, filePath: TemporaryFilePath, - isGenerated: true).WithDesignTimeOnly(true); + isGenerated: true, + loadTextOptions: new LoadTextOptions(ChecksumAlgorithm)) + .WithDesignTimeOnly(true); var projectInfo = ProjectInfo.Create( new ProjectInfo.ProjectAttributes( diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 362f97688f726..f8f628b77365f 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -172,7 +172,6 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( } } - Encoding? defaultEncoding = null; if (pdbCompilationOptions.TryGetValue(Cci.CompilationOptionNames.DefaultEncoding, out var encodingString)) { @@ -311,7 +310,8 @@ private ImmutableArray CreateDocumentInfos( name: Path.GetFileName(info.FilePath), loader: info.Loader, filePath: info.FilePath, - isGenerated: true).WithDesignTimeOnly(true)); + isGenerated: true, + loadTextOptions: new LoadTextOptions(info.ChecksumAlgorithm)).WithDesignTimeOnly(true)); // If we successfully got something from SourceLink for this project then its nice to wait a bit longer // if the user performs subsequent navigation diff --git a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs index 7c14edb1bb04f..a2ff236aead62 100644 --- a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs +++ b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs @@ -50,7 +50,8 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument( name: fileName, loader: textLoader, filePath: filePath, - sourceCodeKind: sourceCodeKind); + sourceCodeKind: sourceCodeKind, + loadTextOptions: new LoadTextOptions(checksumAlgorithm)); // The assembly name must be unique for each collection of loose files. Since the name doesn't matter // a random GUID can be used. diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 59e27ecf372a9..887ace1285f6b 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -125,7 +125,8 @@ DocumentInfo CreateDocumentInfo(string unmappedPath) DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, - loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding)); + loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding), + loadTextOptions: new LoadTextOptions(parsedCommandLine.ChecksumAlgorithm)); } } diff --git a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs index 05d428d755177..b5feee4adaca6 100644 --- a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs @@ -62,7 +62,7 @@ public DocumentInfo CreateDocumentInfo( documentServiceProvider = new RazorDocumentServiceProviderWrapper(razorDocumentServiceProvider); } - return DocumentInfo.Create(id, name, folders, sourceCodeKind, loader, filePath, isGenerated) + return DocumentInfo.Create(id, name, folders, sourceCodeKind, loader, filePath, isGenerated, loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default)) .WithDesignTimeOnly(designTimeOnly) .WithDocumentServiceProvider(documentServiceProvider); } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index eeec5768a2007..1dbad32521290 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -332,7 +332,8 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy documentId, name: filePath, loader: fileLoader, - filePath: filePath)); + filePath: filePath, + loadTextOptions: new LoadTextOptions(projectToAddTo.State.ChecksumAlgorithm))); var addedDocument = forkedSolution.GetRequiredDocument(documentId); diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index cdc7420393e2e..89053a3ac4f35 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs @@ -50,13 +50,10 @@ private sealed class PreviewTextLoader : TextLoader internal PreviewTextLoader(SourceText documentText) => _text = documentText; - private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) - => throw ExceptionUtilities.Unreachable; // checksum alg should never be changed in preview workspace + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(options, cancellationToken)); - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); - - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) => TextAndVersion.Create(_text, VersionStamp.Create()); } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index 88452f03d577c..5f87ccdf69ee9 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -99,7 +99,8 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta folders: folders.IsDefault ? null : folders, sourceCodeKind: sourceCodeKind, loader: textLoader, - filePath: fullPath); + filePath: fullPath, + loadTextOptions: new LoadTextOptions(_project.ChecksumAlgorithm)); using (_project._gate.DisposableWait()) { @@ -149,7 +150,8 @@ public DocumentId AddTextContainer( folders: folders.NullToEmpty(), sourceCodeKind: sourceCodeKind, loader: textLoader, - filePath: fullPath) + filePath: fullPath, + loadTextOptions: new LoadTextOptions(_project.ChecksumAlgorithm)) .WithDesignTimeOnly(designTimeOnly) .WithDocumentServiceProvider(documentServiceProvider); @@ -479,7 +481,7 @@ public void ProcessDynamicFileChange(string projectSystemFilePath, string worksp // Right now we're only supporting dynamic files as actual source files, so it's OK to call GetDocument here var attributes = w.CurrentSolution.GetRequiredDocument(documentId).State.Attributes; - var documentInfo = new DocumentInfo(attributes, fileInfo.TextLoader, fileInfo.DocumentServiceProvider); + var documentInfo = new DocumentInfo(attributes, fileInfo.TextLoader, new LoadTextOptions(_project.ChecksumAlgorithm), fileInfo.DocumentServiceProvider); w.OnDocumentReloaded(documentInfo); }); @@ -581,19 +583,17 @@ private DocumentInfo CreateDocumentInfoFromFileInfo(DynamicFileInfo fileInfo, Im var name = FileNameUtilities.GetFileName(filePath); var documentId = DocumentId.CreateNewId(_project.Id, filePath); - var textLoader = fileInfo.TextLoader; - var documentServiceProvider = fileInfo.DocumentServiceProvider; - return DocumentInfo.Create( documentId, name, folders: folders, sourceCodeKind: fileInfo.SourceCodeKind, - loader: textLoader, + loader: fileInfo.TextLoader, filePath: filePath, - isGenerated: false) + isGenerated: false, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default)) .WithDesignTimeOnly(true) - .WithDocumentServiceProvider(documentServiceProvider); + .WithDocumentServiceProvider(fileInfo.DocumentServiceProvider); } private sealed class SourceTextLoader : TextLoader @@ -607,7 +607,7 @@ public SourceTextLoader(SourceTextContainer textContainer, string? filePath) _filePath = filePath; } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_textContainer.CurrentText, VersionStamp.Create(), _filePath)); } } diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs index 2ac7c7555d8c4..256a82ba867c1 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs @@ -119,7 +119,8 @@ internal ContainedLanguage( documentId, name: filePath, loader: null, - filePath: filePath)); + filePath: filePath, + loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); Workspace.OnDocumentOpened(documentId, SubjectBuffer.AsTextContainer()); } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs index cdfe355a53d2d..12be0f73db32b 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs @@ -87,14 +87,15 @@ public async Task> GetRemoteProjectInfosAsync(Cancel private static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files, SolutionServices services) { var projectId = ProjectId.CreateNewId(); - const SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default; + var checksumAlgorithm = SourceHashAlgorithms.Default; var docInfos = files.SelectAsArray(path => DocumentInfo.Create( DocumentId.CreateNewId(projectId), name: Path.GetFileNameWithoutExtension(path), - loader: new WorkspaceFileTextLoaderNoException(services, path, defaultEncoding: null, checksumAlgorithm), - filePath: path)); + loader: new WorkspaceFileTextLoaderNoException(services, path, defaultEncoding: null), + filePath: path, + loadTextOptions: new LoadTextOptions(checksumAlgorithm))); return ProjectInfo.Create( new ProjectInfo.ProjectAttributes( diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs index acd18d45b2277..03de8a968935a 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs @@ -20,25 +20,19 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects /// internal sealed class WorkspaceFileTextLoaderNoException : WorkspaceFileTextLoader { - private readonly SolutionServices _services; - - public WorkspaceFileTextLoaderNoException(SolutionServices services, string path, Encoding defaultEncoding, SourceHashAlgorithm checksumAlgorithm) + public WorkspaceFileTextLoaderNoException(SolutionServices services, string path, Encoding defaultEncoding) : base(services, path, defaultEncoding) { - _services = services; } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { if (!File.Exists(Path)) { - return Task.FromResult(TextAndVersion.Create(SourceText.From(""), VersionStamp.Create())); + return Task.FromResult(TextAndVersion.Create(SourceText.From("", encoding: null, options.ChecksumAlgorithm), VersionStamp.Create())); } - return base.LoadTextAndVersionAsync(cancellationToken); + return base.LoadTextAndVersionAsync(options, cancellationToken); } - - private protected override TextLoader TryUpdateChecksumAlgorithmImpl(SourceHashAlgorithm algorithm) - => new WorkspaceFileTextLoaderNoException(_services, Path, DefaultEncoding, algorithm); } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index 173b03349e6d9..d872ac914391f 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -349,10 +349,11 @@ private Document AddDocumentToProject(string filePath, string language, string p DocumentId.CreateNewId(project.Id), name: Path.GetFileName(filePath), loader: new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null), - filePath: filePath); + filePath: filePath, + loadTextOptions: new LoadTextOptions(project.State.ChecksumAlgorithm)); OnDocumentAdded(docInfo); - return CurrentSolution.GetDocument(docInfo.Id)!; + return CurrentSolution.GetRequiredDocument(docInfo.Id); } private static string? GetLanguage(string filePath) @@ -383,7 +384,7 @@ public void NotifyOnDocumentClosing(string moniker) // check if the doc is part of the current Roslyn workspace before notifying Roslyn. if (CurrentSolution.ContainsProject(id.ProjectId)) { - OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(Services.SolutionServices, moniker, null, SourceHashAlgorithms.Default)); + OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(Services.SolutionServices, moniker, defaultEncoding: null)); _openedDocs = _openedDocs.Remove(moniker); } } diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs index c16809fb0a6a6..4d7fa069eabc7 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs @@ -59,6 +59,7 @@ internal static SyntaxTree CreateRecoverableTree( string filePath, ParseOptions options, ITextAndVersionSource text, + LoadTextOptions loadTextOptions, Encoding encoding, CompilationUnitSyntax root) { @@ -70,6 +71,7 @@ internal static SyntaxTree CreateRecoverableTree( filePath, options, text, + loadTextOptions, encoding, root.FullSpan.Length, root.ContainsDirectives)); @@ -88,7 +90,7 @@ public override bool TryGetText(out SourceText text) => _info.TryGetText(out text); public override SourceText GetText(CancellationToken cancellationToken) - => _info.TextSource.GetValue(cancellationToken).Text; + => _info.GetText(cancellationToken); public override Task GetTextAsync(CancellationToken cancellationToken) => _info.GetTextAsync(cancellationToken); diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs index 37c091825bfca..26270486f4185 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Diagnostics; using System.IO; using System.Text; using System.Threading; @@ -92,16 +93,17 @@ public override SyntaxTree CreateRecoverableTree( string filePath, ParseOptions options, ITextAndVersionSource text, - Encoding encoding, - SyntaxNode root) + LoadTextOptions loadTextOptions, + Encoding encoding, SyntaxNode root) { - System.Diagnostics.Debug.Assert(CanCreateRecoverableTree(root)); + Debug.Assert(CanCreateRecoverableTree(root)); return RecoverableSyntaxTree.CreateRecoverableTree( this, cacheKey, filePath, options ?? GetDefaultParseOptions(), text, + loadTextOptions, encoding, (CompilationUnitSyntax)root); } diff --git a/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs index a001734996a07..6b4dcc7a1397b 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs @@ -37,7 +37,8 @@ private static Solution GetSolution(params string[] sources) DocumentInfo.Create( DocumentId.CreateNewId(pid), name: "code" + i, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From(s), VersionStamp.Default)))).ToList(); + loader: TextLoader.From(TextAndVersion.Create(SourceText.From(s, encoding: null, SourceHashAlgorithms.Default), VersionStamp.Default)), + loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))).ToList(); var proj = ProjectInfo.Create(pid, VersionStamp.Default, "test", "test.dll", LanguageNames.CSharp, documents: docs, metadataReferences: new[] { TestMetadata.Net451.mscorlib }); diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index 74b7f04078786..3158edc7facec 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -442,6 +442,7 @@ private IEnumerable ResolveAnalyzerReferences(CommandLineArgu private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) { var results = ImmutableArray.CreateBuilder(); + var loadTextOptions = new LoadTextOptions(checksumAlgorithm); foreach (var info in documentFileInfos) { @@ -454,7 +455,8 @@ private ImmutableArray CreateDocumentInfos(IReadOnlyList Microsoft.CodeAnalysis.LoadTextOptions +Microsoft.CodeAnalysis.DocumentInfo.WithLoadTextOptions(Microsoft.CodeAnalysis.LoadTextOptions options) -> Microsoft.CodeAnalysis.DocumentInfo Microsoft.CodeAnalysis.Editing.DeclarationModifiers.IsFile.get -> bool Microsoft.CodeAnalysis.Editing.DeclarationModifiers.WithIsFile(bool isFile) -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers *REMOVED*static Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions @@ -33,10 +35,14 @@ Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentClosedEventAsync(Microsoft.Cod Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentOpenedEventAsync(Microsoft.CodeAnalysis.TextDocument document) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Workspace.TextDocumentClosed -> System.EventHandler Microsoft.CodeAnalysis.Workspace.TextDocumentOpened -> System.EventHandler +override Microsoft.CodeAnalysis.FileTextLoader.CanReloadText.get -> bool override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders = null, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind = Microsoft.CodeAnalysis.SourceCodeKind.Regular, Microsoft.CodeAnalysis.TextLoader loader = null, string filePath = null, bool isGenerated = false, Microsoft.CodeAnalysis.LoadTextOptions? loadTextOptions = null) -> Microsoft.CodeAnalysis.DocumentInfo +static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind, Microsoft.CodeAnalysis.TextLoader loader, string filePath, bool isGenerated) -> Microsoft.CodeAnalysis.DocumentInfo static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText *REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task *REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.TextLoader.CanReloadText.get -> bool virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs index 4f846b8cd017b..25a763e35afce 100644 --- a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs @@ -130,7 +130,7 @@ public Document AddDocument(ProjectId projectId, string name, SourceText text) var id = DocumentId.CreateNewId(projectId); var loader = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create())); - return AddDocument(DocumentInfo.Create(id, name, loader: loader)); + return AddDocument(DocumentInfo.Create(id, name, loader: loader, loadTextOptions: new LoadTextOptions(text.ChecksumAlgorithm))); } /// diff --git a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs index 631d4255a002b..4d60105d2adbe 100644 --- a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs @@ -106,6 +106,8 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, var projectId = ProjectId.CreateNewId(debugName: projectName); + var loadTextOptions = new LoadTextOptions(commandLineArguments.ChecksumAlgorithm); + // construct file infos var docs = new List(); foreach (var fileArg in commandLineArguments.SourceFiles) @@ -128,7 +130,8 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), - filePath: absolutePath); + filePath: absolutePath, + loadTextOptions: loadTextOptions); docs.Add(doc); } @@ -155,7 +158,8 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, folders: folders, sourceCodeKind: SourceCodeKind.Regular, loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), - filePath: absolutePath); + filePath: absolutePath, + loadTextOptions: loadTextOptions); additionalDocs.Add(doc); } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs index 3f6e1c1221840..841c49238fe12 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.AbstractRecoverableSyntaxRoot.cs @@ -20,6 +20,7 @@ internal readonly struct SyntaxTreeInfo public readonly string FilePath; public readonly ParseOptions Options; public readonly ITextAndVersionSource TextSource; + public readonly LoadTextOptions LoadTextOptions; public readonly Encoding Encoding; public readonly int Length; public readonly bool ContainsDirectives; @@ -28,6 +29,7 @@ public SyntaxTreeInfo( string filePath, ParseOptions options, ITextAndVersionSource textSource, + LoadTextOptions loadTextOptions, Encoding encoding, int length, bool containsDirectives) @@ -35,14 +37,15 @@ public SyntaxTreeInfo( FilePath = filePath ?? string.Empty; Options = options; TextSource = textSource; + LoadTextOptions = loadTextOptions; Encoding = encoding; Length = length; ContainsDirectives = containsDirectives; } - internal bool TryGetText(LoadTextOptions options, [NotNullWhen(true)] out SourceText? text) + internal bool TryGetText([NotNullWhen(true)] out SourceText? text) { - if (TextSource.TryGetValue(options, out var textAndVersion)) + if (TextSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { text = textAndVersion.Text; return true; @@ -52,9 +55,12 @@ internal bool TryGetText(LoadTextOptions options, [NotNullWhen(true)] out Source return false; } - internal async Task GetTextAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal SourceText GetText(CancellationToken cancellationToken) + => TextSource.GetValue(LoadTextOptions, cancellationToken).Text; + + internal async Task GetTextAsync(CancellationToken cancellationToken) { - var textAndVersion = await TextSource.GetValueAsync(options, cancellationToken).ConfigureAwait(false); + var textAndVersion = await TextSource.GetValueAsync(LoadTextOptions, cancellationToken).ConfigureAwait(false); return textAndVersion.Text; } @@ -64,6 +70,7 @@ internal SyntaxTreeInfo WithFilePath(string path) path, Options, TextSource, + LoadTextOptions, Encoding, Length, ContainsDirectives); @@ -75,6 +82,7 @@ internal SyntaxTreeInfo WithOptionsAndLengthAndContainsDirectives(ParseOptions o FilePath, options, TextSource, + LoadTextOptions, Encoding, length, containsDirectives); @@ -86,6 +94,7 @@ internal SyntaxTreeInfo WithOptions(ParseOptions options) FilePath, options, TextSource, + LoadTextOptions, Encoding, Length, ContainsDirectives); diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs index 1f7def063dcbe..0398744853e7b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs @@ -40,7 +40,7 @@ public AbstractSyntaxTreeFactoryService(SolutionServices services) public abstract ParseOptions TryParsePdbParseOptions(IReadOnlyDictionary metadata); public abstract SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, SyntaxNode root); public abstract SyntaxTree ParseSyntaxTree(string filePath, ParseOptions options, SourceText text, CancellationToken cancellationToken); - public abstract SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string filePath, ParseOptions options, ITextAndVersionSource text, Encoding encoding, SyntaxNode root); + public abstract SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string filePath, ParseOptions options, ITextAndVersionSource text, LoadTextOptions loadTextOptions, Encoding encoding, SyntaxNode root); public abstract SyntaxNode DeserializeNodeFrom(Stream stream, CancellationToken cancellationToken); public virtual bool CanCreateRecoverableTree(SyntaxNode root) diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs index 2089e82cfb222..5792dee379bb9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs @@ -37,7 +37,7 @@ internal interface ISyntaxTreeFactoryService : ILanguageService bool CanCreateRecoverableTree(SyntaxNode root); // new recoverable tree from root node - SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string? filePath, ParseOptions options, ITextAndVersionSource text, Encoding? encoding, SyntaxNode root); + SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string? filePath, ParseOptions options, ITextAndVersionSource text, LoadTextOptions loadTextOptions, Encoding? encoding, SyntaxNode root); SyntaxNode DeserializeNodeFrom(Stream stream, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs index 5f29f9f42c9c8..69c21e66ee47b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs @@ -20,6 +20,9 @@ public ConstantTextAndVersionSource(TextAndVersion value) { } + public bool CanReloadText + => false; + public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) => GetValue(cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 963ce8f02747f..9159db514394a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -19,10 +19,7 @@ namespace Microsoft.CodeAnalysis /// [DebuggerDisplay("{GetDebuggerDisplay() , nq}")] public sealed class DocumentInfo - { - private readonly TextLoader _textLoader; - internal readonly LoadTextOptions LoadTextOptions; - + { internal DocumentAttributes Attributes { get; } /// @@ -55,6 +52,16 @@ public sealed class DocumentInfo /// public bool IsGenerated => Attributes.IsGenerated; + /// + /// A loader that can retrieve the document text. + /// + public TextLoader? TextLoader { get; } + + /// + /// Options used to load . + /// + public LoadTextOptions LoadTextOptions { get; } + /// /// A associated with this document /// @@ -63,10 +70,10 @@ public sealed class DocumentInfo /// /// Create a new instance of a . /// - internal DocumentInfo(DocumentAttributes attributes, TextLoader loader, LoadTextOptions loadTextOptions, IDocumentServiceProvider? documentServiceProvider) + internal DocumentInfo(DocumentAttributes attributes, TextLoader? loader, LoadTextOptions loadTextOptions, IDocumentServiceProvider? documentServiceProvider) { Attributes = attributes; - _textLoader = loader; + TextLoader = loader; LoadTextOptions = loadTextOptions; DocumentServiceProvider = documentServiceProvider; } @@ -74,6 +81,17 @@ internal DocumentInfo(DocumentAttributes attributes, TextLoader loader, LoadText /// /// Creates info. /// + [Obsolete] + public static DocumentInfo Create( + DocumentId id, + string name, + IEnumerable? folders, + SourceCodeKind sourceCodeKind, + TextLoader? loader, + string? filePath, + bool isGenerated) + => Create(id, name, folders, sourceCodeKind, loader, filePath, isGenerated, loadTextOptions: null); + public static DocumentInfo Create( DocumentId id, string name, @@ -81,7 +99,8 @@ public static DocumentInfo Create( SourceCodeKind sourceCodeKind = SourceCodeKind.Regular, TextLoader? loader = null, string? filePath = null, - bool isGenerated = false) + bool isGenerated = false, + LoadTextOptions? loadTextOptions = null) { return new DocumentInfo( new DocumentAttributes( @@ -92,24 +111,24 @@ public static DocumentInfo Create( filePath, isGenerated, designTimeOnly: false), - loader ?? NullTextLoader.Default, - new LoadTextOptions(SourceHashAlgorithm.Sha1), + loader, + loadTextOptions ?? new LoadTextOptions(SourceHashAlgorithms.Default), documentServiceProvider: null); } private DocumentInfo With( DocumentAttributes? attributes = null, - TextLoader? loader = null, + Optional loader = default, Optional loadTextOptions = default, Optional documentServiceProvider = default) { var newAttributes = attributes ?? Attributes; - var newLoader = loader ?? _textLoader; + var newLoader = loader.HasValue ? loader.Value : TextLoader; var newLoadTextOptions = loadTextOptions.HasValue ? loadTextOptions.Value : LoadTextOptions; var newDocumentServiceProvider = documentServiceProvider.HasValue ? documentServiceProvider.Value : DocumentServiceProvider; if (newAttributes == Attributes && - newLoader == _textLoader && + newLoader == TextLoader && newLoadTextOptions == LoadTextOptions && newDocumentServiceProvider == DocumentServiceProvider) { @@ -119,12 +138,6 @@ private DocumentInfo With( return new DocumentInfo(newAttributes, newLoader, newLoadTextOptions, newDocumentServiceProvider); } - /// - /// A loader that can retrieve the document text. - /// - public TextLoader? TextLoader - => _textLoader is NullTextLoader ? null : _textLoader; - public DocumentInfo WithId(DocumentId id) => With(attributes: Attributes.With(id: id ?? throw new ArgumentNullException(nameof(id)))); @@ -141,7 +154,7 @@ public DocumentInfo WithFilePath(string? filePath) => With(attributes: Attributes.With(filePath: filePath)); public DocumentInfo WithTextLoader(TextLoader? loader) - => With(loader: loader ?? NullTextLoader.Default); + => With(loader: loader); public DocumentInfo WithLoadTextOptions(LoadTextOptions options) => With(loadTextOptions: options); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index e5a828feea27b..9e168e73b7ba5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -83,6 +83,7 @@ public DocumentState( } [MemberNotNullWhen(true, nameof(_treeSource))] + [MemberNotNullWhen(true, nameof(_options))] internal bool SupportsSyntaxTree => _treeSource != null; @@ -201,7 +202,7 @@ private static TreeAndVersion CreateTreeAndVersion( var root = tree.GetRoot(cancellationToken); if (mode == PreservationMode.PreserveValue && treeFactory.CanCreateRecoverableTree(root)) { - tree = treeFactory.CreateRecoverableTree(cacheKey, tree.FilePath, tree.Options, newTextSource, text.Encoding, root); + tree = treeFactory.CreateRecoverableTree(cacheKey, tree.FilePath, tree.Options, newTextSource, new LoadTextOptions(text.ChecksumAlgorithm), text.Encoding, root); } Contract.ThrowIfNull(tree); @@ -339,14 +340,26 @@ public DocumentState UpdateChecksumAlgorithm(SourceHashAlgorithm checksumAlgorit { var newLoadTextOptions = new LoadTextOptions(checksumAlgorithm); - // TODO: we should be able to reuse the tree - var newTreeSource = CreateLazyFullyParsedTree( + if (LoadTextOptions == newLoadTextOptions) + { + return this; + } + + // To keep the loaded SourceText consistent with the DocumentState, + // avoid updating the options if the loader can't apply them on the loaded SourceText. + if (!TextAndVersionSource.CanReloadText) + { + return this; + } + + // TODO: we should be able to reuse the tree root + var newTreeSource = SupportsSyntaxTree ? CreateLazyFullyParsedTree( TextAndVersionSource, newLoadTextOptions, Id.ProjectId, GetSyntaxTreeFilePath(Attributes), _options, - _languageServices); + _languageServices) : null; return new DocumentState( LanguageServices, @@ -480,7 +493,7 @@ public DocumentState UpdateFilePath(string? filePath) LoadTextOptions, Id.ProjectId, GetSyntaxTreeFilePath(newAttributes), - _options!, + _options, _languageServices) : null; return new DocumentState( @@ -505,7 +518,7 @@ protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSou { ValueSource? newTreeSource; - if (_treeSource == null) + if (!SupportsSyntaxTree) { newTreeSource = null; } @@ -520,7 +533,7 @@ protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSou LoadTextOptions, Id.ProjectId, GetSyntaxTreeFilePath(Attributes), - _options!, + _options, _languageServices, mode); // TODO: understand why the mode is given here. If we're preserving text by identity, why also preserve the tree? } @@ -654,8 +667,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT tree.GetText, cacheResult: true), textVersion, - filePath, - checksumAlgorithm); + filePath); } else { @@ -674,10 +686,9 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT cancellationToken => tree.GetRoot(cancellationToken).GetText(encoding, checksumAlgorithm), cacheResult: false)), textVersion, - filePath, - checksumAlgorithm); + filePath); - tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, filePath, options, lazyTextAndVersion, encoding, newRoot); + tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, filePath, options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); } return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion, checksumAlgorithm)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs index a2f12caaf7f0d..343884e0a0855 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs @@ -21,14 +21,14 @@ private sealed class TreeTextSource : ITextAndVersionSource, ITextVersionable private readonly VersionStamp _version; private readonly string _filePath; - public SourceHashAlgorithm ChecksumAlgorithm { get; } + public bool CanReloadText + => false; - public TreeTextSource(ValueSource textSource, VersionStamp version, string filePath, SourceHashAlgorithm checksumAlgorithm) + public TreeTextSource(ValueSource textSource, VersionStamp version, string filePath) { _textSource = textSource; _version = version; _filePath = filePath; - ChecksumAlgorithm = checksumAlgorithm; } public async Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs index 76f0c709f2f43..4455d69a2efba 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -18,6 +20,8 @@ namespace Microsoft.CodeAnalysis [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] public class FileTextLoader : TextLoader { + private static readonly ConditionalWeakTable> s_isObsoleteCreateTextOverriden = new(); + /// /// Absolute path of the file. /// @@ -58,6 +62,14 @@ public FileTextLoader(string path, Encoding? defaultEncoding) internal sealed override string FilePath => Path; + public override bool CanReloadText => !IsObsoleteCreateTextOverridden; + +#pragma warning disable CS0618 // Type or member is obsolete + private bool IsObsoleteCreateTextOverridden + => s_isObsoleteCreateTextOverriden.GetValue( + GetType(), _ => new StrongBox(new Func(CreateText).Method.DeclaringType != typeof(FileTextLoader))).Value; +#pragma warning restore + /// /// Creates from . /// @@ -65,14 +77,16 @@ public FileTextLoader(string path, Encoding? defaultEncoding) /// Obsolete. Null. [Obsolete("Use CreateText(Stream, CancellationToken)")] protected virtual SourceText CreateText(Stream stream, Workspace? workspace) - => EncodedStringText.Create(stream, DefaultEncoding); + => EncodedStringText.Create(stream, DefaultEncoding, checksumAlgorithm: SourceHashAlgorithms.Default); /// /// Creates from . /// - protected virtual SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) #pragma warning disable CS0618 // Type or member is obsolete - => CreateText(stream, workspace: null); + protected virtual SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) + => IsObsoleteCreateTextOverridden ? + CreateText(stream, workspace: null) : + EncodedStringText.Create(stream, DefaultEncoding, checksumAlgorithm: options.ChecksumAlgorithm); #pragma warning restore /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs index 8fca535535e05..c6b8eb49fb6d8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs @@ -11,6 +11,11 @@ namespace Microsoft.CodeAnalysis; internal interface ITextAndVersionSource { + /// + /// True if can be reloaded. + /// + bool CanReloadText { get; } + bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value); TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken); Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs index 02eec8be2c010..ba38c0915c6e8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs @@ -44,11 +44,8 @@ public LoadableTextAndVersionSource(TextLoader loader, bool cacheResult) CacheResult = cacheResult; } - /// - /// True if the text content can be reloaded from the underlying binary representation (e.g. on disk). - /// - public bool IsReloadable - => Loader is FileTextLoader; + public bool CanReloadText + => Loader.CanReloadText; private AsyncLazy GetLazyValue(LoadTextOptions options) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs deleted file mode 100644 index 93bc6cd2b2997..0000000000000 --- a/src/Workspaces/Core/Portable/Workspace/Solution/NullTextLoader.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis; - -/// -/// that does not load text. -/// -internal sealed class NullTextLoader : TextLoader -{ - public static readonly NullTextLoader Default = new(); - - public NullTextLoader() - { - } - - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) - => throw new NotImplementedException(); -} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs index 1d09386492ebb..0820a140c7626 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -24,10 +24,13 @@ internal sealed class RecoverableTextAndVersion : ITextVersionable, ITextAndVers // At that point the initial source is no longer referenced and can be garbage collected. private object _initialSourceOrRecoverableText; + public bool CanReloadText { get; } + public RecoverableTextAndVersion(ITextAndVersionSource initialSource, SolutionServices services) { _initialSourceOrRecoverableText = initialSource; _services = services; + CanReloadText = initialSource.CanReloadText; } private bool TryGetInitialSourceOrRecoverableText([NotNullWhen(true)] out ITextAndVersionSource? source, [NotNullWhen(false)] out RecoverableText? text) @@ -152,10 +155,10 @@ public RecoverableText(ITextAndVersionSource source, TextAndVersion textAndVersi LoadDiagnostic = textAndVersion.LoadDiagnostic; LoadTextOptions = options; - if (source is LoadableTextAndVersionSource { IsReloadable: true } reloadableSource) + if (source.CanReloadText) { // reloadable source must not cache results - Contract.ThrowIfTrue(reloadableSource.CacheResult); + Contract.ThrowIfTrue(source is LoadableTextAndVersionSource { CacheResult: true }); InitialSource = source; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index 2cbbb2ea414a1..9bda96b3a9a61 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1071,7 +1071,8 @@ private Solution AddDocumentImpl(ProjectState project, DocumentId documentId, st sourceCodeKind: GetSourceCodeKind(project), loader: TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create(), name)), filePath: filePath, - isGenerated: isGenerated)); + isGenerated: isGenerated, + loadTextOptions: new LoadTextOptions(project.ChecksumAlgorithm))); /// /// Creates a new solution instance with the project updated to include a new document with @@ -1095,7 +1096,8 @@ public Solution AddDocument(DocumentId documentId, string name, TextLoader loade name, folders, GetSourceCodeKind(project), - loader)); + loader, + loadTextOptions: new LoadTextOptions(project.ChecksumAlgorithm))); } /// @@ -1207,7 +1209,8 @@ private DocumentInfo CreateDocumentInfo(DocumentId documentId, string name, Sour folders: folders, sourceCodeKind: GetSourceCodeKind(project), loader: loader, - filePath: filePath); + filePath: filePath, + loadTextOptions: new LoadTextOptions(text.ChecksumAlgorithm)); } private ProjectState GetRequiredProjectState(ProjectId projectId) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index 576489be532da..36c9299f6b1b0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs @@ -85,7 +85,6 @@ public SourceGeneratedDocumentState WithUpdatedGeneratedContent(SourceText sourc return Create( Identity, sourceText, - LoadTextOptions, parseOptions, this.LanguageServices, this.solutionServices); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index 5cf0b67604220..b449e5b5e0d8b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Drawing; using System.IO; using System.Reflection; @@ -31,12 +32,18 @@ public abstract class TextLoader internal virtual string? FilePath => null; + /// + /// True if reloads from its original binary representation (e.g. file on disk). + /// + public virtual bool CanReloadText + => false; + /// /// Load a text and a version of the document. /// /// - /// Use when creating from a stream. - /// Ignore if the is not created from a binary stream. + /// Use when creating from an original binary representation. + /// Ignore if the is not created from a binary representation. /// /// Cancellation token. /// diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 286d79761735f..f07c6c7146d39 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -1629,7 +1629,7 @@ private void ApplyChangedDocument( // ApplyDocumentInfoChanged ignores the loader information, so we can pass null for it ApplyDocumentInfoChanged( documentId, - new DocumentInfo(newDoc.State.Attributes, NullTextLoader.Default, documentServiceProvider: newDoc.State.Services)); + new DocumentInfo(newDoc.State.Attributes, loader: null, newDoc.State.LoadTextOptions, documentServiceProvider: newDoc.State.Services)); } } @@ -1668,7 +1668,8 @@ internal static DocumentInfo CreateDocumentInfoWithoutText(TextDocument doc) doc is Document sourceDoc ? sourceDoc.SourceCodeKind : SourceCodeKind.Regular, loader: null, filePath: doc.FilePath, - isGenerated: doc.State.Attributes.IsGenerated) + isGenerated: doc.State.Attributes.IsGenerated, + loadTextOptions: doc.State.LoadTextOptions) .WithDesignTimeOnly(doc.State.Attributes.DesignTimeOnly) .WithDocumentServiceProvider(doc.Services); diff --git a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs index 4522d5f48efbb..4a4fbed5e38e4 100644 --- a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs @@ -47,7 +47,7 @@ public void Create() Assert.Equal(SourceCodeKind.Script, info.SourceCodeKind); Assert.Same(loader, info.TextLoader); Assert.True(info.IsGenerated); - Assert.Equal(SourceHashAlgorithms.Default, info.ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithms.Default, info.LoadTextOptions.ChecksumAlgorithm); } [Fact] diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index d621c53bf6867..1943ac74ef3c1 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -684,37 +684,37 @@ public void WithProjectChecksumAlgorithm_DocumentUpdates() solution = solution.AddDocument(documentAId, "a.cs", textLoaderA); solution = solution.AddDocument(documentBId, "b.cs", "class B {}"); solution = solution.AddDocument(documentCId, "c.cs", textC); - solution = solution.AddDocument(documentDId, "d.cs", new FileTextLoader(fileD.Path, defaultEncoding: null, SourceHashAlgorithm.Sha1)); + solution = solution.AddDocument(documentDId, "d.cs", new FileTextLoader(fileD.Path, defaultEncoding: null)); - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentAId).GetTextSynchronously(default).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentBId).GetTextSynchronously(default).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentCId).GetTextSynchronously(default).ChecksumAlgorithm); - - var fileText = solution.GetRequiredDocument(documentDId).GetTextSynchronously(default); - Assert.Equal(SourceHashAlgorithm.Sha1, fileText.ChecksumAlgorithm); - Assert.Equal(checksumSHA1, fileText.GetChecksum()); + Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentDId), SourceHashAlgorithm.Sha1, checksumSHA1); + // only file loader based documents support updating checksum alg: solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha256); + Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentDId), SourceHashAlgorithm.Sha256, checksumSHA256); // only file loader based documents support updating checksum alg: - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentAId).GetTextSynchronously(default).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentBId).GetTextSynchronously(default).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentCId).GetTextSynchronously(default).ChecksumAlgorithm); - - fileText = solution.GetRequiredDocument(documentDId).GetTextSynchronously(default); - Assert.Equal(SourceHashAlgorithm.Sha256, fileText.ChecksumAlgorithm); - Assert.Equal(checksumSHA256, fileText.GetChecksum()); - solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); + Verify(solution.GetRequiredDocument(documentDId), SourceHashAlgorithm.Sha1, checksumSHA1); - // only file loader based documents support updating checksum alg: - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentAId).GetTextSynchronously(default).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentBId).GetTextSynchronously(default).ChecksumAlgorithm); - Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetRequiredDocument(documentCId).GetTextSynchronously(default).ChecksumAlgorithm); + static void Verify(Document document, SourceHashAlgorithm expectedAlgorithm, byte[]? expectedChecksum = null) + { + Assert.Equal(expectedAlgorithm, document.State.LoadTextOptions.ChecksumAlgorithm); + Assert.Equal(expectedAlgorithm, document.GetTextSynchronously(default).ChecksumAlgorithm); - fileText = solution.GetRequiredDocument(documentDId).GetTextSynchronously(default); - Assert.Equal(SourceHashAlgorithm.Sha1, fileText.ChecksumAlgorithm); - Assert.Equal(checksumSHA1, fileText.GetChecksum()); + if (expectedChecksum != null) + { + Assert.Equal(expectedChecksum, document.GetTextSynchronously(default).GetChecksum()); + } + } } [Fact] @@ -3133,9 +3133,7 @@ public void TestProjectCompletenessWithMultipleProjects() private sealed class TestSmallFileTextLoader : FileTextLoader { public TestSmallFileTextLoader(string path, Encoding encoding) -#pragma warning disable RS0030 // Do not used banned APIs - : base(path, encoding, SourceHashAlgorithms.Default) -#pragma warning restore + : base(path, encoding) { } @@ -3159,7 +3157,7 @@ public async Task TestMassiveFileSize() try { // test async one - var unused = await loader.LoadTextAndVersionAsync(CancellationToken.None); + var unused = await loader.LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None); } catch (InvalidDataException ex) { @@ -3173,7 +3171,7 @@ public async Task TestMassiveFileSize() try { // test sync one - var unused = loader.LoadTextAndVersionSynchronously(CancellationToken.None); + var unused = loader.LoadTextAndVersionSynchronously(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None); } catch (InvalidDataException ex) { @@ -3195,7 +3193,8 @@ public void TestWithSyntaxTree() var factory = dummyProject.Services.GetService(); // create the origin tree - var strongTree = factory.ParseSyntaxTree("dummy", dummyProject.ParseOptions, SourceText.From("// empty", encoding: null, SourceHashAlgorithms.Default), CancellationToken.None); + var text = SourceText.From("// empty", encoding: null, SourceHashAlgorithms.Default); + var strongTree = factory.ParseSyntaxTree("dummy", dummyProject.ParseOptions, text, CancellationToken.None); // create recoverable tree off the original tree var sourceText = strongTree.GetText(); @@ -3204,8 +3203,8 @@ public void TestWithSyntaxTree() strongTree.FilePath, strongTree.Options, new ConstantTextAndVersionSource(TextAndVersion.Create(sourceText, VersionStamp.Create(), strongTree.FilePath)), - sourceText.Encoding, - strongTree.GetRoot()); + new LoadTextOptions(text.ChecksumAlgorithm), + sourceText.Encoding, strongTree.GetRoot()); // create new tree before it ever getting root node var newTree = recoverableTree.WithFilePath("different/dummy"); diff --git a/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs index 117532c6db3e6..5df444234c8fc 100644 --- a/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs @@ -99,7 +99,7 @@ private class LoaderOverridesNew : TextLoader { public static readonly TextAndVersion Value = TextAndVersion.Create(SourceText.From(""), VersionStamp.Default); - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(Value); } @@ -107,7 +107,7 @@ public override Task LoadTextAndVersionAsync(CancellationToken c [MemberData(nameof(GetNoOverideLoaders))] public async Task NoOverride(TextLoader loader) { - await Assert.ThrowsAsync(() => loader.LoadTextAndVersionAsync(CancellationToken.None)); + await Assert.ThrowsAsync(() => loader.LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None)); await Assert.ThrowsAsync(() => loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); } @@ -115,7 +115,7 @@ public async Task NoOverride(TextLoader loader) public async Task OverridesObsolete() { var loader = new LoaderOverridesObsolete(); - Assert.Same(LoaderOverridesObsolete.Value, await loader.LoadTextAndVersionAsync(CancellationToken.None)); + Assert.Same(LoaderOverridesObsolete.Value, await loader.LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None)); Assert.Same(LoaderOverridesObsolete.Value, await loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); } @@ -123,7 +123,7 @@ public async Task OverridesObsolete() public async Task OverridesObsolete2() { var loader = new LoaderOverridesObsolete2(); - Assert.Same(LoaderOverridesObsolete2.Value, await loader.LoadTextAndVersionAsync(CancellationToken.None)); + Assert.Same(LoaderOverridesObsolete2.Value, await loader.LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None)); Assert.Same(LoaderOverridesObsolete2.Value, await loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); } @@ -131,7 +131,7 @@ public async Task OverridesObsolete2() public async Task OverridesNew() { var loader = new LoaderOverridesNew(); - Assert.Same(LoaderOverridesNew.Value, await loader.LoadTextAndVersionAsync(CancellationToken.None)); + Assert.Same(LoaderOverridesNew.Value, await loader.LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None)); Assert.Same(LoaderOverridesNew.Value, await loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); } } diff --git a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs index 1f75b2756aabe..b21164fce03e3 100644 --- a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs +++ b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs @@ -13,17 +13,14 @@ namespace Roslyn.Test.Utilities { internal class TestTextLoader : TextLoader { - private readonly string _text; - - internal override SourceHashAlgorithm ChecksumAlgorithm { get; } + private readonly TextAndVersion _textAndVersion; public TestTextLoader(string text = "test", SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default) { - _text = text; - ChecksumAlgorithm = checksumAlgorithm; + _textAndVersion = TextAndVersion.Create(SourceText.From(text, encoding: null, checksumAlgorithm), VersionStamp.Create()); } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(TextAndVersion.Create(SourceText.From(_text, encoding: null, ChecksumAlgorithm), VersionStamp.Create())); + public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(_textAndVersion); } } diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index de76bba8839a5..1323326174113 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -2365,10 +2365,10 @@ public async Task TestLoadTextSync() var infos = await loader.LoadProjectInfoAsync(projectFullPath); var doc = infos[0].Documents[0]; - var tav = doc.TextLoader.LoadTextAndVersionSynchronously(CancellationToken.None); + var tav = doc.TextLoader.LoadTextAndVersionSynchronously(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None); var adoc = infos[0].AdditionalDocuments.First(a => a.Name == "XamlFile.xaml"); - var atav = adoc.TextLoader.LoadTextAndVersionSynchronously(CancellationToken.None); + var atav = adoc.TextLoader.LoadTextAndVersionSynchronously(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None); Assert.Contains("Window", atav.Text.ToString(), StringComparison.Ordinal); } diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 4c2fb6aac5256..b006552b8e428 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -88,14 +88,11 @@ public async Task CreateDocumentInfoAsync(Checksum documentChecksu var attributes = await GetAssetAsync(documentSnapshot.Info, cancellationToken).ConfigureAwait(false); var serializableSourceText = await GetAssetAsync(documentSnapshot.Text, cancellationToken).ConfigureAwait(false); - var textLoader = TextLoader.From( - TextAndVersion.Create( - await serializableSourceText.GetTextAsync(cancellationToken).ConfigureAwait(false), - VersionStamp.Create(), - attributes.FilePath)); + var text = await serializableSourceText.GetTextAsync(cancellationToken).ConfigureAwait(false); + var textLoader = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create(), attributes.FilePath)); // TODO: do we need version? - return new DocumentInfo(attributes, textLoader, documentServiceProvider: null); + return new DocumentInfo(attributes, textLoader, new LoadTextOptions(text.ChecksumAlgorithm), documentServiceProvider: null); } private async Task> CreateDocumentInfosAsync(ChecksumCollection documentChecksums, CancellationToken cancellationToken) diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb index 0ca52560d6b47..69dfca8f5891d 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb @@ -2,6 +2,7 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Runtime.InteropServices Imports System.Text Imports System.Threading Imports Microsoft.CodeAnalysis.Host @@ -53,6 +54,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic filePath As String, options As ParseOptions, text As ITextAndVersionSource, + loadTextOptions As LoadTextOptions, encoding As Encoding, root As VisualBasicSyntaxNode) As SyntaxTree Return New RecoverableSyntaxTree( @@ -63,6 +65,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic filePath, options, text, + loadTextOptions, encoding, root.FullSpan.Length, root.ContainsDirectives)) @@ -86,16 +89,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - Public Overrides Function TryGetText(options As LoadTextOptions, ByRef text As SourceText) As Boolean - Return _info.TryGetText(options, text) + Public Overrides Function TryGetText( ByRef text As SourceText) As Boolean + Return _info.TryGetText(text) End Function - Public Overrides Function GetText(options As LoadTextOptions, cancellationToken As CancellationToken) As SourceText - Return _info.TextSource.GetValue(options, cancellationToken).Text + Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText + Return _info.GetText(cancellationToken) End Function - Public Overrides Function GetTextAsync(options As LoadTextOptions, cancellationToken As CancellationToken) As Task(Of SourceText) - Return _info.GetTextAsync(options, cancellationToken) + Public Overrides Function GetTextAsync(Optional cancellationToken As CancellationToken = Nothing) As Task(Of SourceText) + Return _info.GetTextAsync(cancellationToken) End Function Public Overrides ReadOnly Property Encoding As Encoding diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb index 08c66404dcc3b..4a902b57aca50 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb @@ -101,6 +101,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic filePath As String, optionsOpt As ParseOptions, text As ITextAndVersionSource, + loadTextOptions As LoadTextOptions, encoding As Encoding, root As SyntaxNode) As SyntaxTree @@ -111,6 +112,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic filePath, If(optionsOpt, GetDefaultParseOptions()), text, + loadTextOptions, encoding, DirectCast(root, CompilationUnitSyntax)) End Function From 9f1667614faf219fe7ede7f8f59f084e31d61737 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 08:53:18 -0700 Subject: [PATCH 13/32] Fixes --- .../VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb | 2 ++ .../MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs | 3 ++- src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt | 1 + .../Core/Portable/Workspace/Solution/DocumentInfo.cs | 2 +- .../Core/Portable/Workspace/WorkspaceFileTextLoader.cs | 2 ++ 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb index f6fb702ec1128..7b0ac70f0a796 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb @@ -34,7 +34,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Optional path As String = "", Optional encoding As Encoding = Nothing) As SyntaxTree +#Disable Warning RS0030 ' Do not used banned APIs Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), path, encoding) +#Enable Warning End Function ''' diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index f729a8433e3c2..7c94bfda5af21 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -80,7 +80,8 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work loader: TextLoader.From(assemblyInfoSourceText.Container, VersionStamp.Default), filePath: null, isGenerated: true, - loadTextOptions: new LoadTextOptions(assemblyInfoSourceText.ChecksumAlgorithm)).WithDesignTimeOnly(true); + loadTextOptions: new LoadTextOptions(assemblyInfoSourceText.ChecksumAlgorithm)) + .WithDesignTimeOnly(true); var generatedDocumentId = DocumentId.CreateNewId(projectId); var generatedDocument = DocumentInfo.Create( diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index c111c54004f51..7e6546a8b59a7 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -37,6 +37,7 @@ Microsoft.CodeAnalysis.Workspace.TextDocumentClosed -> System.EventHandler System.EventHandler override Microsoft.CodeAnalysis.FileTextLoader.CanReloadText.get -> bool override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +*REMOVED*static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders = null, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind = Microsoft.CodeAnalysis.SourceCodeKind.Regular, Microsoft.CodeAnalysis.TextLoader loader = null, string filePath = null, bool isGenerated = false) -> Microsoft.CodeAnalysis.DocumentInfo static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders = null, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind = Microsoft.CodeAnalysis.SourceCodeKind.Regular, Microsoft.CodeAnalysis.TextLoader loader = null, string filePath = null, bool isGenerated = false, Microsoft.CodeAnalysis.LoadTextOptions? loadTextOptions = null) -> Microsoft.CodeAnalysis.DocumentInfo static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind, Microsoft.CodeAnalysis.TextLoader loader, string filePath, bool isGenerated) -> Microsoft.CodeAnalysis.DocumentInfo static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 9159db514394a..42558716ba65c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis /// [DebuggerDisplay("{GetDebuggerDisplay() , nq}")] public sealed class DocumentInfo - { + { internal DocumentAttributes Attributes { get; } /// diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs index b3e2c29dff10a..10a96bb39170d 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs @@ -20,7 +20,9 @@ internal class WorkspaceFileTextLoader : FileTextLoader private readonly ITextFactoryService _textFactory; internal WorkspaceFileTextLoader(SolutionServices services, string path, Encoding? defaultEncoding) +#pragma warning disable RS0030 // Do not used banned APIs : base(path, defaultEncoding) +#pragma warning restore { _textFactory = services.GetRequiredService(); } From b4c162face9895ad0cc5b7421cd616e9a9c72ddc Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 08:53:32 -0700 Subject: [PATCH 14/32] Better banned symbols comment --- eng/config/BannedSymbols.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/eng/config/BannedSymbols.txt b/eng/config/BannedSymbols.txt index b47447f7ecde3..c73ba8a519afd 100644 --- a/eng/config/BannedSymbols.txt +++ b/eng/config/BannedSymbols.txt @@ -35,17 +35,17 @@ M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAna M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.Text.TextSpan,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,System.Collections.Generic.IEnumerable{Microsoft.CodeAnalysis.Text.TextSpan},Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Editing.SyntaxEditor.#ctor(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.Host.HostWorkspaceServices); Use overload that takes HostSolutionServices instead -M:Microsoft.CodeAnalysis.FileTextLoader.#ctor(System.String,System.Text.Encoding); use WorkspaceFileTextLoader -M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm -M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken) -M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode); Use CSharpSyntaxTree.Create -M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions options, System.String path, System.Text.Encoding encoding); Use overload with checksum algorithm +M:Microsoft.CodeAnalysis.FileTextLoader.#ctor(System.String,System.Text.Encoding); use WorkspaceFileTextLoader that calls on ITextFactoryService to create SourceText +M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use CSharpSyntaxTree sublass that takes checksum algorithm +M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken); Use CSharpSyntaxTree sublass that takes checksum algorithm +M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode); Use CSharpSyntaxTree sublass that takes checksum algorithm +M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions options, System.String path, System.Text.Encoding encoding); Use CSharpSyntaxTree sublass that takes checksum algorithm M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.CSharp.CSharpParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken); Use API that takes SourceText M:Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.CSharp.CSharpParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Nullable{System.Boolean},System.Threading.CancellationToken); Use API that takes SourceText -M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm +M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.SyntaxTree(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding); Use VisualBasicSyntaxTree sublass that takes checksum algorithm M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Threading.CancellationToken); Use overload with SourceText M:Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(System.String,Microsoft.CodeAnalysis.ParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Threading.CancellationToken); Use overload with SourceText -M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode); Use VisualBasicSyntaxTree.Create -M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.Create(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic}); Use overload with checksum algorithm -M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.Create(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding); Use overload with checksum algorithm +M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.CreateWithoutClone(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode); Use VisualBasicSyntaxTree sublass that takes checksum algorithm +M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.Create(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic}); Use VisualBasicSyntaxTree sublass that takes checksum algorithm +M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.Create(Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding); Use VisualBasicSyntaxTree sublass that takes checksum algorithm M:Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.ParseText(System.String,Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions,System.String,System.Text.Encoding,System.Collections.Immutable.ImmutableDictionary{System.String,Microsoft.CodeAnalysis.ReportDiagnostic},System.Threading.CancellationToken); Use overload with SourceText From 696934824a5604dd4966ded3b36f18208d4163e3 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 09:15:13 -0700 Subject: [PATCH 15/32] Fix --- .../Core/Portable/Workspace/Solution/TextLoader.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index b449e5b5e0d8b..cfac32acb7522 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -9,6 +9,7 @@ using System.Drawing; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -24,7 +25,7 @@ namespace Microsoft.CodeAnalysis /// public abstract class TextLoader { - private static ImmutableDictionary s_isObsoleteLoadTextAndVersionAsyncOverriden = ImmutableDictionary.Empty; + private static readonly ConditionalWeakTable> s_isObsoleteLoadTextAndVersionAsyncOverriden = new(); private const double MaxDelaySecs = 1.0; private const int MaxRetries = 5; @@ -52,16 +53,15 @@ public virtual bool CanReloadText public virtual Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { #pragma warning disable CS0618 // Type or member is obsolete - if (ImmutableInterlocked.GetOrAdd( - ref s_isObsoleteLoadTextAndVersionAsyncOverriden, + if (s_isObsoleteLoadTextAndVersionAsyncOverriden.GetValue( GetType(), - _ => new Func>(LoadTextAndVersionAsync).Method.DeclaringType != typeof(TextLoader))) + _ => new StrongBox(new Func>(LoadTextAndVersionAsync).Method.DeclaringType != typeof(TextLoader))).Value) { return LoadTextAndVersionAsync(workspace: null, documentId: null, cancellationToken); } #pragma warning restore - throw new NotImplementedException($"{GetType()} must override LoadTextAndVersionAsync"); + throw new NotImplementedException($"{GetType()} must override {nameof(LoadTextAndVersionAsync)}"); } /// @@ -74,7 +74,7 @@ public virtual Task LoadTextAndVersionAsync(LoadTextOptions opti /// [Obsolete("Use LoadTextAndVersionAsync(CancellationToken) instead")] public virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) - => throw new NotImplementedException("The API is obsolete, call LoadTextAndVersionAsync(CancellationToken) instead"); + => LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), cancellationToken); /// /// Load a text and a version of the document in the workspace. From 2540a5e215ed7c31937a7716f0f74cc09e8766c6 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 09:35:02 -0700 Subject: [PATCH 16/32] Remove checksum alg from TreeAndVersion --- .../Core/Portable/Workspace/Solution/DocumentState.cs | 11 +++++------ .../Portable/Workspace/Solution/TreeAndVersion.cs | 5 +---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index 9e168e73b7ba5..466073762dd44 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -209,7 +209,7 @@ private static TreeAndVersion CreateTreeAndVersion( CheckTree(tree, text); // text version for this document should be unique. use it as a starting point. - return new TreeAndVersion(tree, textAndVersion.Version, text.ChecksumAlgorithm); + return new TreeAndVersion(tree, textAndVersion.Version); } private static ValueSource CreateLazyIncrementallyParsedTree( @@ -287,7 +287,7 @@ private static TreeAndVersion MakeNewTreeAndVersion(SyntaxTree oldTree, SourceTe { var topLevelChanged = TopLevelChanged(oldTree, oldText, newTree, newText); var version = topLevelChanged ? newVersion : oldVersion; - return new TreeAndVersion(newTree, version, newText.ChecksumAlgorithm); + return new TreeAndVersion(newTree, version); } private const int MaxTextChangeRangeLength = 1024 * 4; @@ -418,11 +418,11 @@ private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocesso else if (existingTree.TryGetRoot(out var existingRoot) && !existingRoot.ContainsDirectives) { var treeFactory = _languageServices.GetRequiredService(); - newTree = treeFactory.CreateSyntaxTree(FilePath, options, existingTree.Encoding, existingTreeAndVersion.ChecksumAlgorithm, existingRoot); + newTree = treeFactory.CreateSyntaxTree(GetSyntaxTreeFilePath(Attributes), options, existingTree.Encoding, LoadTextOptions.ChecksumAlgorithm, existingRoot); } if (newTree is not null) - newTreeSource = new ConstantValueSource(new TreeAndVersion(newTree, existingTreeAndVersion.Version, existingTreeAndVersion.ChecksumAlgorithm)); + newTreeSource = new ConstantValueSource(new TreeAndVersion(newTree, existingTreeAndVersion.Version)); } // If we weren't able to reuse in a smart way, just reparse @@ -609,7 +609,6 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) Contract.ThrowIfNull(_options); var (text, treeAndVersion) = CreateRecoverableTextAndTree(newRoot, filePath, newTextVersion, newTreeVersion, encoding, LoadTextOptions.ChecksumAlgorithm, Attributes, _options, syntaxTreeFactory, mode); - Debug.Assert(treeAndVersion.ChecksumAlgorithm == LoadTextOptions.ChecksumAlgorithm); return new DocumentState( LanguageServices, @@ -691,7 +690,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, filePath, options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); } - return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion, checksumAlgorithm)); + return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion)); } internal override Task GetLoadDiagnosticAsync(CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs index 7cb487b232d9d..fc6b7e6928be0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TreeAndVersion.cs @@ -22,13 +22,10 @@ internal sealed class TreeAndVersion /// public VersionStamp Version { get; } - public SourceHashAlgorithm ChecksumAlgorithm { get; } - - public TreeAndVersion(SyntaxTree tree, VersionStamp version, SourceHashAlgorithm checksumAlgorithm) + public TreeAndVersion(SyntaxTree tree, VersionStamp version) { Tree = tree; Version = version; - ChecksumAlgorithm = checksumAlgorithm; } } } From 1fbbb1c338bfdbfc9d79d6225a68f31a7b9f0709 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 09:44:27 -0700 Subject: [PATCH 17/32] Fix file paths --- .../Portable/Workspace/Solution/DocumentState.cs | 15 +++++---------- .../Solution/DocumentState_TreeTextSource.cs | 10 ++++------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index 466073762dd44..6b5c5ab1d4a6f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -605,10 +605,8 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) var syntaxTreeFactory = _languageServices.GetRequiredService(); - var filePath = GetSyntaxTreeFilePath(Attributes); - Contract.ThrowIfNull(_options); - var (text, treeAndVersion) = CreateRecoverableTextAndTree(newRoot, filePath, newTextVersion, newTreeVersion, encoding, LoadTextOptions.ChecksumAlgorithm, Attributes, _options, syntaxTreeFactory, mode); + var (text, treeAndVersion) = CreateRecoverableTextAndTree(newRoot, newTextVersion, newTreeVersion, encoding, LoadTextOptions.ChecksumAlgorithm, Attributes, _options, syntaxTreeFactory, mode); return new DocumentState( LanguageServices, @@ -642,7 +640,6 @@ private VersionStamp GetNewTreeVersionForUpdatedTree(SyntaxNode newRoot, Version // use static method so we don't capture references to this private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndTree( SyntaxNode newRoot, - string filePath, VersionStamp textVersion, VersionStamp treeVersion, Encoding? encoding, @@ -657,7 +654,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT if (mode == PreservationMode.PreserveIdentity || !factory.CanCreateRecoverableTree(newRoot)) { - tree = factory.CreateSyntaxTree(GetSyntaxTreeFilePath(attributes), options, encoding, checksumAlgorithm, newRoot); + tree = factory.CreateSyntaxTree(GetSyntaxTreeFilePath(Attributes), options, encoding, checksumAlgorithm, newRoot); // its okay to use a strong cached AsyncLazy here because the compiler layer SyntaxTree will also keep the text alive once its built. lazyTextAndVersion = new TreeTextSource( @@ -665,8 +662,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT tree.GetTextAsync, tree.GetText, cacheResult: true), - textVersion, - filePath); + textVersion); } else { @@ -684,10 +680,9 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT async cancellationToken => (await tree.GetRootAsync(cancellationToken).ConfigureAwait(false)).GetText(encoding, checksumAlgorithm), cancellationToken => tree.GetRoot(cancellationToken).GetText(encoding, checksumAlgorithm), cacheResult: false)), - textVersion, - filePath); + textVersion); - tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, filePath, options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); + tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, GetSyntaxTreeFilePath(Attributes), options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); } return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs index 343884e0a0855..16bc4f27b467c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs @@ -19,35 +19,33 @@ private sealed class TreeTextSource : ITextAndVersionSource, ITextVersionable { private readonly ValueSource _textSource; private readonly VersionStamp _version; - private readonly string _filePath; public bool CanReloadText => false; - public TreeTextSource(ValueSource textSource, VersionStamp version, string filePath) + public TreeTextSource(ValueSource textSource, VersionStamp version) { _textSource = textSource; _version = version; - _filePath = filePath; } public async Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) { var text = await _textSource.GetValueAsync(cancellationToken).ConfigureAwait(false); - return TextAndVersion.Create(text, _version, _filePath); + return TextAndVersion.Create(text, _version); } public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) { var text = _textSource.GetValue(cancellationToken); - return TextAndVersion.Create(text, _version, _filePath); + return TextAndVersion.Create(text, _version); } public bool TryGetValue(LoadTextOptions options, [NotNullWhen(true)] out TextAndVersion? value) { if (_textSource.TryGetValue(out var text)) { - value = TextAndVersion.Create(text, _version, _filePath); + value = TextAndVersion.Create(text, _version); return true; } else From eedc84aed527530c787b6e6df64a993dd3b16e94 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 11:39:04 -0700 Subject: [PATCH 18/32] More feedback --- .../Portable/Syntax/CSharpSyntaxNode.cs | 2 +- .../CSharp/Portable/Syntax/SyntaxFactory.cs | 9 +-- .../Portable/Syntax/SyntaxNodeFactories.vb | 6 +- .../Portable/Syntax/VisualBasicSyntaxNode.vb | 2 +- ...ntaxTreeFactoryService.ParsedSyntaxTree.cs | 2 +- .../Workspace/Solution/DocumentState.cs | 4 +- .../Workspace/Solution/ProjectState.cs | 12 ---- .../Solution/RecoverableTextAndVersion.cs | 3 + .../Portable/Workspace/Solution/Solution.cs | 17 ------ .../Workspace/Solution/SolutionState.cs | 17 ------ .../CoreTest/SolutionTests/SolutionTests.cs | 16 +++--- .../Host/RemoteWorkspace.SolutionCreator.cs | 56 ++++++++++++++++++- .../Compiler/Core/Utilities/AsyncLazy`1.cs | 2 - ...ntaxTreeFactoryService.ParsedSyntaxTree.vb | 2 +- 14 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs index 8c121c0981826..6fe8a9047c379 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs @@ -72,7 +72,7 @@ private static SyntaxTree ComputeSyntaxTree(CSharpSyntaxNode node) if (parent == null) { // set the tree on the root node atomically -#pragma warning disable RS0030 // This method is intended to be used from this call site only +#pragma warning disable RS0030 // Do not use banned APIs (CreateWithoutClone is intended to be used from this call site only) Interlocked.CompareExchange(ref node._syntaxTree, CSharpSyntaxTree.CreateWithoutClone(node), null); #pragma warning restore tree = node._syntaxTree; diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index b11a471db811a..45da303a817a5 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1544,15 +1544,10 @@ public static IdentifierNameSyntax IdentifierName(string name) /// Create a new syntax tree from a syntax node. /// public static SyntaxTree SyntaxTree(SyntaxNode root, ParseOptions? options = null, string path = "", Encoding? encoding = null) - { -#pragma warning disable RS0030 // Do not used banned APIs - return CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions?)options, path, encoding); -#pragma warning restore - } + => CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions?)options, path, encoding, SourceHashAlgorithm.Sha1); #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters #pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. -#pragma warning disable RS0030 // Do not used banned APIs /// public static SyntaxTree ParseSyntaxTree( string text, @@ -1561,7 +1556,7 @@ public static SyntaxTree ParseSyntaxTree( Encoding? encoding = null, CancellationToken cancellationToken = default) { - return CSharpSyntaxTree.ParseText(text, (CSharpParseOptions?)options, path, encoding, cancellationToken); + return CSharpSyntaxTree.ParseText(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), (CSharpParseOptions?)options, path, diagnosticOptions: null, isGeneratedCode: null, cancellationToken) } #pragma warning restore diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb index 7b0ac70f0a796..37407ad412a37 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb @@ -34,9 +34,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Optional path As String = "", Optional encoding As Encoding = Nothing) As SyntaxTree -#Disable Warning RS0030 ' Do not used banned APIs - Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), path, encoding) -#Enable Warning + Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), path, encoding, SourceHashAlgorithm.Sha1) End Function ''' @@ -49,7 +47,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic encoding As Encoding, cancellationToken As CancellationToken) As SyntaxTree - Return ParseSyntaxTree(SourceText.From(text, encoding), options, path, cancellationToken) + Return ParseSyntaxTree(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), options, path, cancellationToken) End Function ''' diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb index 8eeee3522ee02..cddea06481803 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb @@ -69,7 +69,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If tree Is Nothing Then Debug.Assert(rootCandidate IsNot Nothing) -#Disable Warning RS0030 ' This method is intended to be used from this call site only +#Disable Warning RS0030 ' Do not use banned APIs (CreateWithoutClone is intended to be used from this call site only) tree = VisualBasicSyntaxTree.CreateWithoutClone(DirectCast(rootCandidate, VisualBasicSyntaxNode)) #Enable Warning RS0030 End If diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs index 9673769034bb0..fb4a502503426 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs @@ -70,7 +70,7 @@ public override bool TryGetRoot([NotNullWhen(true)] out CSharpSyntaxNode? root) } public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options) - => (root == _root && options == Options) ? this : new ParsedSyntaxTree(lazyText: null, (CSharpSyntaxNode)root, (CSharpParseOptions)options, FilePath, Encoding, _checksumAlgorithm); + => (root == _root && options == Options) ? this : new ParsedSyntaxTree((root == _root) ? _lazyText : null, (CSharpSyntaxNode)root, (CSharpParseOptions)options, FilePath, Encoding, _checksumAlgorithm); public override SyntaxTree WithFilePath(string path) => (path == FilePath) ? this : new ParsedSyntaxTree(_lazyText, _root, Options, path, Encoding, _checksumAlgorithm); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index 6b5c5ab1d4a6f..be860f8fda64e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -654,7 +654,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT if (mode == PreservationMode.PreserveIdentity || !factory.CanCreateRecoverableTree(newRoot)) { - tree = factory.CreateSyntaxTree(GetSyntaxTreeFilePath(Attributes), options, encoding, checksumAlgorithm, newRoot); + tree = factory.CreateSyntaxTree(GetSyntaxTreeFilePath(attributes), options, encoding, checksumAlgorithm, newRoot); // its okay to use a strong cached AsyncLazy here because the compiler layer SyntaxTree will also keep the text alive once its built. lazyTextAndVersion = new TreeTextSource( @@ -682,7 +682,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT cacheResult: false)), textVersion); - tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, GetSyntaxTreeFilePath(Attributes), options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); + tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, GetSyntaxTreeFilePath(attributes), options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); } return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 1fac42a205ed0..dbe71043c6e23 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -705,18 +705,6 @@ private ProjectState WithNewerAttributes(ProjectInfo.ProjectAttributes attribute return With(projectInfo: ProjectInfo.With(attributes: attributes)); } - public ProjectState WithAttributes(ProjectInfo.ProjectAttributes attributes, bool updateDocuments) - { - if (attributes == Attributes) - { - return this; - } - - return With( - projectInfo: ProjectInfo.With(attributes: attributes.With(version: Version.GetNewerVersion())), - documentStates: updateDocuments ? UpdateDocumentsChecksumAlgorithm(attributes.ChecksumAlgorithm) : null); - } - public ProjectState WithName(string name) => (name == Name) ? this : WithNewerAttributes(Attributes.With(name: name, version: Version.GetNewerVersion())); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs index 0820a140c7626..4eaa9d6b06bff 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -33,6 +33,9 @@ public RecoverableTextAndVersion(ITextAndVersionSource initialSource, SolutionSe CanReloadText = initialSource.CanReloadText; } + /// + /// True if the is available, false if is returned. + /// private bool TryGetInitialSourceOrRecoverableText([NotNullWhen(true)] out ITextAndVersionSource? source, [NotNullWhen(false)] out RecoverableText? text) { // store to local to avoid race: diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index 9bda96b3a9a61..a7b629f09ddf8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -566,23 +566,6 @@ internal Solution WithRunAnalyzers(ProjectId projectId, bool runAnalyzers) return new Solution(newState); } - /// - /// Creates a new solution instance with the project specified updated to have the attributes, - /// except for which is auto-incremented. - /// - internal Solution WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes, bool updateDocuments) - { - CheckContainsProject(projectId); - - var newState = _state.WithProjectAttributes(projectId, attributes, updateDocuments); - if (newState == _state) - { - return this; - } - - return new Solution(newState); - } - /// /// Creates a new solution instance with the project documents in the order by the specified document ids. /// The specified document ids must be the same as what is already in the project; no adding or removing is allowed. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 3bd0618d5ada4..9f2abca5ea1e9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -831,23 +831,6 @@ public SolutionState WithRunAnalyzers(ProjectId projectId, bool runAnalyzers) return ForkProject(newProject); } - /// - /// Create a new solution instance with the project specified updated to have - /// the specified attributes, except for which is auto-incremented. - /// - public SolutionState WithProjectAttributes(ProjectId projectId, ProjectInfo.ProjectAttributes attributes, bool updateDocuments) - { - var oldProject = GetRequiredProjectState(projectId); - var newProject = oldProject.WithAttributes(attributes, updateDocuments); - - if (oldProject == newProject) - { - return this; - } - - return ForkProject(newProject); - } - /// /// Create a new solution instance with the project specified updated to include /// the specified project references. diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 1943ac74ef3c1..167fc41d8db66 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -659,7 +659,7 @@ public void WithProjectChecksumAlgorithm_DocumentUpdates() var documentAId = DocumentId.CreateNewId(projectId); var documentBId = DocumentId.CreateNewId(projectId); var documentCId = DocumentId.CreateNewId(projectId); - var documentDId = DocumentId.CreateNewId(projectId); + var fileDocumentId = DocumentId.CreateNewId(projectId); var fileD = Temp.CreateFile(); var bytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true).GetBytes("Text"); @@ -677,33 +677,33 @@ public void WithProjectChecksumAlgorithm_DocumentUpdates() var textLoaderA = new TestTextLoader("class A {}", SourceHashAlgorithm.Sha1); var textC = SourceText.From("class C {}", encoding: null, checksumAlgorithm: SourceHashAlgorithm.Sha1); - var solution = workspace.CurrentSolution. - AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp). - WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); + var solution = workspace.CurrentSolution + .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp) + .WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); solution = solution.AddDocument(documentAId, "a.cs", textLoaderA); solution = solution.AddDocument(documentBId, "b.cs", "class B {}"); solution = solution.AddDocument(documentCId, "c.cs", textC); - solution = solution.AddDocument(documentDId, "d.cs", new FileTextLoader(fileD.Path, defaultEncoding: null)); + solution = solution.AddDocument(fileDocumentId, "d.cs", new FileTextLoader(fileD.Path, defaultEncoding: null)); Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentDId), SourceHashAlgorithm.Sha1, checksumSHA1); + Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha1, checksumSHA1); // only file loader based documents support updating checksum alg: solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha256); Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentDId), SourceHashAlgorithm.Sha256, checksumSHA256); + Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha256, checksumSHA256); // only file loader based documents support updating checksum alg: solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentDId), SourceHashAlgorithm.Sha1, checksumSHA1); + Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha1, checksumSHA1); static void Verify(Document document, SourceHashAlgorithm expectedAlgorithm, byte[]? expectedChecksum = null) { diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index 343991df88700..732ccdf2b76d4 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -300,10 +300,62 @@ private async Task UpdateProjectInfoAsync(Project project, Checksum inf Contract.ThrowIfFalse(project.State.ProjectInfo.Attributes.Language == newProjectAttributes.Language); Contract.ThrowIfFalse(project.State.ProjectInfo.Attributes.IsSubmission == newProjectAttributes.IsSubmission); - // do not propagate attribute update to documents - they will be updated by UpdateDocumentsAsync: - return project.Solution.WithProjectAttributes(project.Id, newProjectAttributes, updateDocuments: false).GetRequiredProject(project.Id); + var projectId = project.Id; + + if (project.State.ProjectInfo.Attributes.Name != newProjectAttributes.Name) + { + project = project.Solution.WithProjectName(projectId, newProjectAttributes.Name).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.AssemblyName != newProjectAttributes.AssemblyName) + { + project = project.Solution.WithProjectAssemblyName(projectId, newProjectAttributes.AssemblyName).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.FilePath != newProjectAttributes.FilePath) + { + project = project.Solution.WithProjectFilePath(projectId, newProjectAttributes.FilePath).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.OutputFilePath != newProjectAttributes.OutputFilePath) + { + project = project.Solution.WithProjectOutputFilePath(projectId, newProjectAttributes.OutputFilePath).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.OutputRefFilePath != newProjectAttributes.OutputRefFilePath) + { + project = project.Solution.WithProjectOutputRefFilePath(projectId, newProjectAttributes.OutputRefFilePath).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.CompilationOutputInfo != newProjectAttributes.CompilationOutputInfo) + { + project = project.Solution.WithProjectCompilationOutputInfo(project.Id, newProjectAttributes.CompilationOutputInfo).GetProject(project.Id)!; + } + + if (project.State.ProjectInfo.Attributes.DefaultNamespace != newProjectAttributes.DefaultNamespace) + { + project = project.Solution.WithProjectDefaultNamespace(projectId, newProjectAttributes.DefaultNamespace).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.HasAllInformation != newProjectAttributes.HasAllInformation) + { + project = project.Solution.WithHasAllInformation(projectId, newProjectAttributes.HasAllInformation).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.RunAnalyzers != newProjectAttributes.RunAnalyzers) + { + project = project.Solution.WithRunAnalyzers(projectId, newProjectAttributes.RunAnalyzers).GetProject(projectId)!; + } + + if (project.State.ProjectInfo.Attributes.ChecksumAlgorithm != newProjectAttributes.ChecksumAlgorithm) + { + project = project.Solution.WithProjectChecksumAlgorithm(projectId, newProjectAttributes.ChecksumAlgorithm).GetProject(projectId)!; + } + + return project; } + private async Task UpdateDocumentsAsync( Project project, ProjectStateChecksums projectChecksums, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs index f3d59fa21e145..beb42443acbcf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs @@ -117,8 +117,6 @@ public AsyncLazy(Func> asynchronousComputeFunction, F _cacheResult = cacheResult; } - public bool CacheResult => _cacheResult; - #region Lock Wrapper for Invariant Checking /// diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb index 878418ef03514..208f45861a712 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb @@ -72,7 +72,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides Function WithRootAndOptions(root As SyntaxNode, options As ParseOptions) As SyntaxTree Return If(ReferenceEquals(root, _root) AndAlso options = Me.Options, Me, - New ParsedSyntaxTree(lazyText:=Nothing, DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), FilePath, Encoding, _checksumAlgorithm)) + New ParsedSyntaxTree(If(ReferenceEquals(root, _root), _lazyText, Nothing), DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), FilePath, Encoding, _checksumAlgorithm)) End Function Public Overrides Function WithFilePath(path As String) As SyntaxTree From 099a9383e43b54b02980215556679e3516ddfb37 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 12:45:26 -0700 Subject: [PATCH 19/32] Fixes --- src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs | 5 +++-- .../VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index 45da303a817a5..6c9b5f5db1d50 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1544,10 +1544,11 @@ public static IdentifierNameSyntax IdentifierName(string name) /// Create a new syntax tree from a syntax node. /// public static SyntaxTree SyntaxTree(SyntaxNode root, ParseOptions? options = null, string path = "", Encoding? encoding = null) - => CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions?)options, path, encoding, SourceHashAlgorithm.Sha1); + => CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions?)options ?? CSharpParseOptions.Default, path, encoding, SourceHashAlgorithm.Sha1); #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters #pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. +#pragma warning disable CS0618 // Type or member is obsolete /// public static SyntaxTree ParseSyntaxTree( string text, @@ -1556,7 +1557,7 @@ public static SyntaxTree ParseSyntaxTree( Encoding? encoding = null, CancellationToken cancellationToken = default) { - return CSharpSyntaxTree.ParseText(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), (CSharpParseOptions?)options, path, diagnosticOptions: null, isGeneratedCode: null, cancellationToken) + return CSharpSyntaxTree.ParseText(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), (CSharpParseOptions?)options, path, diagnosticOptions: null, isGeneratedCode: null, cancellationToken); } #pragma warning restore diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb index 37407ad412a37..e52b227fafd52 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb @@ -34,7 +34,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Optional path As String = "", Optional encoding As Encoding = Nothing) As SyntaxTree - Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), path, encoding, SourceHashAlgorithm.Sha1) + Return VisualBasicSyntaxTree.Create(DirectCast(root, VisualBasicSyntaxNode), If(DirectCast(options, VisualBasicParseOptions), VisualBasicParseOptions.Default), path, encoding, SourceHashAlgorithm.Sha1) End Function ''' From bd66d171c8e011d31d348a0ee4c9f4884c1be70d Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 12:59:50 -0700 Subject: [PATCH 20/32] Remove DocumentInfo.LoadTextOptions --- .../RazorLineFormattingOptionsTests.cs | 3 +- .../EditAndContinueWorkspaceServiceTests.cs | 53 ++++++++----------- .../TestUtilities/Rename/RenamerTests.cs | 3 +- .../Workspaces/TestHostDocument.cs | 2 +- .../Configuration/ConfigurationUpdater.cs | 3 +- .../EditAndContinue/CommittedSolution.cs | 3 +- .../AbstractGenerateTypeService.Editor.cs | 3 +- .../MetadataAsSourceGeneratedFileInfo.cs | 6 +-- ...rceDocumentMetadataAsSourceFileProvider.cs | 4 +- .../Workspace/MiscellaneousFileUtilities.cs | 3 +- .../Lsif/Generator/CompilerInvocation.cs | 3 +- .../RazorLanguageServerFactoryWrapper.cs | 2 +- .../Implementation/AbstractEditorFactory.cs | 3 +- ...tudioProject.BatchingDocumentCollection.cs | 11 ++-- .../Core/Def/Venus/ContainedLanguage.cs | 3 +- .../RoslynRemoteProjectInfoProvider.cs | 3 +- .../Client/RemoteLanguageServiceWorkspace.cs | 3 +- .../CodeGeneration/SymbolEditorTests.cs | 3 +- .../MSBuild/MSBuildProjectLoader.Worker.cs | 12 ++--- .../Core/Portable/PublicAPI.Unshipped.txt | 3 -- .../Core/Portable/Workspace/AdhocWorkspace.cs | 2 +- .../Portable/Workspace/CommandLineProject.cs | 6 +-- .../Solution/AdditionalDocumentState.cs | 3 +- .../Solution/AnalyzerConfigDocumentState.cs | 3 +- .../Workspace/Solution/DocumentInfo.cs | 31 ++--------- .../Workspace/Solution/DocumentState.cs | 3 +- .../Workspace/Solution/ProjectState.cs | 11 ++-- .../Portable/Workspace/Solution/Solution.cs | 9 ++-- .../Workspace/Solution/SolutionState.cs | 8 +-- .../Workspace/Solution/TextDocumentState.cs | 6 +-- .../Core/Portable/Workspace/Workspace.cs | 5 +- .../SolutionTests/DocumentInfoTests.cs | 1 - .../Remote/Core/AbstractAssetProvider.cs | 2 +- 33 files changed, 80 insertions(+), 139 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs index 788e0e7ae482a..cd5f5e8c6ef8c 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs @@ -60,8 +60,7 @@ void F () {} folders: Array.Empty(), sourceCodeKind: SourceCodeKind.Regular, loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), "file.razor.g.cs")), - filePath: "file.razor.g.cs", - loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default)) + filePath: "file.razor.g.cs") .WithDesignTimeOnly(true) .WithDocumentServiceProvider(new TestRazorDocumentServiceProvider()); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index eda7dad424a4c..9e80a1286db65 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -369,8 +369,7 @@ private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, st sourceCodeKind: SourceCodeKind.Regular, loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), path)), filePath: path, - isGenerated: false, - loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) + isGenerated: false) .WithDesignTimeOnly(true); } @@ -493,40 +492,38 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume // prepare workspace as if it was loaded from project files: using var _ = CreateWorkspace(out var solution, out var service, new[] { typeof(NoCompilationLanguageService) }); - var projectP = solution.AddProject("P", "P", LanguageNames.CSharp); - solution = projectP.Solution; + var projectPId = ProjectId.CreateNewId(); + solution = solution + .AddProject(projectPId, "P", "P", LanguageNames.CSharp) + .WithProjectChecksumAlgorithm(projectPId, SourceHashAlgorithm.Sha1); - var documentIdA = DocumentId.CreateNewId(projectP.Id, debugName: "A"); + var documentIdA = DocumentId.CreateNewId(projectPId, debugName: "A"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA), - filePath: sourceFileA.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); + filePath: sourceFileA.Path)); - var documentIdB = DocumentId.CreateNewId(projectP.Id, debugName: "B"); + var documentIdB = DocumentId.CreateNewId(projectPId, debugName: "B"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdB, name: "B", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), - filePath: sourceFileB.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); + filePath: sourceFileB.Path)); - var documentIdC = DocumentId.CreateNewId(projectP.Id, debugName: "C"); + var documentIdC = DocumentId.CreateNewId(projectPId, debugName: "C"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdC, name: "C", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC), - filePath: sourceFileC.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); + filePath: sourceFileC.Path)); - var documentIdE = DocumentId.CreateNewId(projectP.Id, debugName: "E"); + var documentIdE = DocumentId.CreateNewId(projectPId, debugName: "E"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdE, name: "E", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE), - filePath: sourceFileE.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha1))); + filePath: sourceFileE.Path)); // check that we are testing documents whose hash algorithm does not match the PDB (but the hash itself does): Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdA).GetTextSynchronously(default).ChecksumAlgorithm); @@ -536,8 +533,8 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume // design-time-only document with and without absolute path: solution = solution. - AddDocument(CreateDesignTimeOnlyDocument(projectP.Id, name: "dt1.cs", path: Path.Combine(dir.Path, "dt1.cs"))). - AddDocument(CreateDesignTimeOnlyDocument(projectP.Id, name: "dt2.cs", path: "dt2.cs")); + AddDocument(CreateDesignTimeOnlyDocument(projectPId, name: "dt1.cs", path: Path.Combine(dir.Path, "dt1.cs"))). + AddDocument(CreateDesignTimeOnlyDocument(projectPId, name: "dt2.cs", path: "dt2.cs")); // project that does not support EnC - the contents of documents in this project shouldn't be loaded: var projectQ = solution.AddProject("Q", "Q", NoCompilationConstants.LanguageName); @@ -547,8 +544,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume id: DocumentId.CreateNewId(projectQ.Id, debugName: "D"), name: "D", loader: new FailingTextLoader(), - filePath: sourceFileD.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithm.Sha256))); + filePath: sourceFileD.Path)); var captureMatchingDocuments = captureAllDocuments ? ImmutableArray.Empty : @@ -573,7 +569,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Equal(ModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); - AssertEx.Equal(new[] { $"{projectP.Id}: Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFileB.Path)}" }, InspectDiagnostics(emitDiagnostics)); + AssertEx.Equal(new[] { $"{projectPId}: Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFileB.Path)}" }, InspectDiagnostics(emitDiagnostics)); EndDebuggingSession(debuggingSession); } @@ -727,8 +723,7 @@ public async Task DesignTimeOnlyDocument_Dynamic() name: "design-time-only.cs", loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), "design-time-only.cs")), filePath: "design-time-only.cs", - isGenerated: false, - loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) + isGenerated: false) .WithDesignTimeOnly(true); solution = solution.AddDocument(documentInfo); @@ -3642,8 +3637,7 @@ public async Task ActiveStatements_ForeignDocument(bool withPath, bool designTim DocumentId.CreateNewId(project.Id, "test"), name: "test", loader: TextLoader.From(TextAndVersion.Create(sourceText, VersionStamp.Create(), filePath)), - filePath: filePath, - loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) + filePath: filePath) .WithDesignTimeOnly(designTimeOnly); var document = project.Solution.AddDocument(documentInfo).GetDocument(documentInfo.Id); @@ -4405,8 +4399,7 @@ public async Task MultiSession() id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), - filePath: sourceFileA.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); + filePath: sourceFileA.Path)); var tasks = Enumerable.Range(0, 10).Select(async i => { @@ -4492,8 +4485,7 @@ public async Task WatchHotReloadServiceTest() id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), - filePath: sourceFileA.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); + filePath: sourceFileA.Path)); var hotReload = new WatchHotReloadService(workspace.Services, ImmutableArray.Create("Baseline", "AddDefinitionToExistingType", "NewTypeDefinition")); @@ -4560,8 +4552,7 @@ public async Task UnitTestingHotReloadServiceTest() id: documentIdA, name: "A", loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), - filePath: sourceFileA.Path, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); + filePath: sourceFileA.Path)); var hotReload = new UnitTestingHotReloadService(workspace.Services); diff --git a/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs b/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs index dabb1b73e3f54..7a8e3d2875a09 100644 --- a/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs +++ b/src/EditorFeatures/TestUtilities/Rename/RenamerTests.cs @@ -228,8 +228,7 @@ protected async Task TestRenameMappedFile(string startText, string documentName, folders: GetDocumentFolders(s_defaultDocumentPath), loader: TextLoader.From(TextAndVersion.Create(startSourceText, VersionStamp.Create(), documentName)), filePath: s_defaultDocumentPath, - isGenerated: true, - loadTextOptions: new LoadTextOptions(startSourceText.ChecksumAlgorithm)) + isGenerated: true) .WithDocumentServiceProvider(new TestDocumentServiceProvider()); solution = solution.AddDocument(documentInfo); diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index c64891ac58263..c719b70842226 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -358,7 +358,7 @@ internal void CloseTextView() public DocumentInfo ToDocumentInfo() { Contract.ThrowIfTrue(IsSourceGenerated, "We shouldn't be producing a DocumentInfo for a source generated document."); - return DocumentInfo.Create(Id, Name, Folders, SourceCodeKind, Loader, FilePath, isGenerated: false, new LoadTextOptions(ChecksumAlgorithm)) + return DocumentInfo.Create(Id, Name, Folders, SourceCodeKind, Loader, FilePath, isGenerated: false) .WithDocumentServiceProvider(_documentServiceProvider); } } diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs index 1beb98cff1655..a2d1ef502c60e 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs @@ -341,8 +341,7 @@ private async Task ConfigureAsync() var documentInfo = DocumentInfo.Create( id, name: ".editorconfig", - filePath: analyzerConfigPath, - loadTextOptions: new LoadTextOptions(project.State.ChecksumAlgorithm)); + filePath: analyzerConfigPath); var newSolution = project.Solution.AddAnalyzerConfigDocuments(ImmutableArray.Create(documentInfo)); return newSolution.GetProject(project.Id)?.GetAnalyzerConfigDocument(id); diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index ab245d16261c4..2230f13e062b6 100644 --- a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs +++ b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs @@ -292,8 +292,7 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel folders: document.Folders, loader: TextLoader.From(TextAndVersion.Create(sourceText, sourceTextVersion, document.Name)), filePath: document.FilePath, - isGenerated: document.State.Attributes.IsGenerated, - loadTextOptions: new LoadTextOptions(sourceText.ChecksumAlgorithm)) + isGenerated: document.State.Attributes.IsGenerated) .WithDesignTimeOnly(document.State.Attributes.DesignTimeOnly) .WithDocumentServiceProvider(document.State.Services)); } diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index 133512feeb8df..f741339d17f8d 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -358,8 +358,7 @@ private async Task> CreateAddDocumentAndUpdateU documentId, documentName, containers, - sourceCodeKind, - loadTextOptions: new LoadTextOptions(projectToBeUpdated.State.ChecksumAlgorithm))); + sourceCodeKind)); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(documentId, root, PreservationMode.PreserveIdentity); diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index 7c94bfda5af21..eddc0c911096c 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -79,8 +79,7 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work assemblyInfoFileName, loader: TextLoader.From(assemblyInfoSourceText.Container, VersionStamp.Default), filePath: null, - isGenerated: true, - loadTextOptions: new LoadTextOptions(assemblyInfoSourceText.ChecksumAlgorithm)) + isGenerated: true) .WithDesignTimeOnly(true); var generatedDocumentId = DocumentId.CreateNewId(projectId); @@ -89,8 +88,7 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work Path.GetFileName(TemporaryFilePath), loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding) : null, filePath: TemporaryFilePath, - isGenerated: true, - loadTextOptions: new LoadTextOptions(ChecksumAlgorithm)) + isGenerated: true) .WithDesignTimeOnly(true); var projectInfo = ProjectInfo.Create( diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index f8f628b77365f..c733d0fb87c12 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -310,8 +310,8 @@ private ImmutableArray CreateDocumentInfos( name: Path.GetFileName(info.FilePath), loader: info.Loader, filePath: info.FilePath, - isGenerated: true, - loadTextOptions: new LoadTextOptions(info.ChecksumAlgorithm)).WithDesignTimeOnly(true)); + isGenerated: true) + .WithDesignTimeOnly(true)); // If we successfully got something from SourceLink for this project then its nice to wait a bit longer // if the user performs subsequent navigation diff --git a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs index a2ff236aead62..7c14edb1bb04f 100644 --- a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs +++ b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs @@ -50,8 +50,7 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument( name: fileName, loader: textLoader, filePath: filePath, - sourceCodeKind: sourceCodeKind, - loadTextOptions: new LoadTextOptions(checksumAlgorithm)); + sourceCodeKind: sourceCodeKind); // The assembly name must be unique for each collection of loose files. Since the name doesn't matter // a random GUID can be used. diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 887ace1285f6b..59e27ecf372a9 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -125,8 +125,7 @@ DocumentInfo CreateDocumentInfo(string unmappedPath) DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, - loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding), - loadTextOptions: new LoadTextOptions(parsedCommandLine.ChecksumAlgorithm)); + loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding)); } } diff --git a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs index b5feee4adaca6..05d428d755177 100644 --- a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs @@ -62,7 +62,7 @@ public DocumentInfo CreateDocumentInfo( documentServiceProvider = new RazorDocumentServiceProviderWrapper(razorDocumentServiceProvider); } - return DocumentInfo.Create(id, name, folders, sourceCodeKind, loader, filePath, isGenerated, loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default)) + return DocumentInfo.Create(id, name, folders, sourceCodeKind, loader, filePath, isGenerated) .WithDesignTimeOnly(designTimeOnly) .WithDocumentServiceProvider(documentServiceProvider); } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 1dbad32521290..eeec5768a2007 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -332,8 +332,7 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy documentId, name: filePath, loader: fileLoader, - filePath: filePath, - loadTextOptions: new LoadTextOptions(projectToAddTo.State.ChecksumAlgorithm))); + filePath: filePath)); var addedDocument = forkedSolution.GetRequiredDocument(documentId); diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index 5f87ccdf69ee9..a136b5f783f13 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -99,8 +99,7 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta folders: folders.IsDefault ? null : folders, sourceCodeKind: sourceCodeKind, loader: textLoader, - filePath: fullPath, - loadTextOptions: new LoadTextOptions(_project.ChecksumAlgorithm)); + filePath: fullPath); using (_project._gate.DisposableWait()) { @@ -150,8 +149,7 @@ public DocumentId AddTextContainer( folders: folders.NullToEmpty(), sourceCodeKind: sourceCodeKind, loader: textLoader, - filePath: fullPath, - loadTextOptions: new LoadTextOptions(_project.ChecksumAlgorithm)) + filePath: fullPath) .WithDesignTimeOnly(designTimeOnly) .WithDocumentServiceProvider(documentServiceProvider); @@ -481,7 +479,7 @@ public void ProcessDynamicFileChange(string projectSystemFilePath, string worksp // Right now we're only supporting dynamic files as actual source files, so it's OK to call GetDocument here var attributes = w.CurrentSolution.GetRequiredDocument(documentId).State.Attributes; - var documentInfo = new DocumentInfo(attributes, fileInfo.TextLoader, new LoadTextOptions(_project.ChecksumAlgorithm), fileInfo.DocumentServiceProvider); + var documentInfo = new DocumentInfo(attributes, fileInfo.TextLoader, fileInfo.DocumentServiceProvider); w.OnDocumentReloaded(documentInfo); }); @@ -590,8 +588,7 @@ private DocumentInfo CreateDocumentInfoFromFileInfo(DynamicFileInfo fileInfo, Im sourceCodeKind: fileInfo.SourceCodeKind, loader: fileInfo.TextLoader, filePath: filePath, - isGenerated: false, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default)) + isGenerated: false) .WithDesignTimeOnly(true) .WithDocumentServiceProvider(fileInfo.DocumentServiceProvider); } diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs index 256a82ba867c1..2ac7c7555d8c4 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs @@ -119,8 +119,7 @@ internal ContainedLanguage( documentId, name: filePath, loader: null, - filePath: filePath, - loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))); + filePath: filePath)); Workspace.OnDocumentOpened(documentId, SubjectBuffer.AsTextContainer()); } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs index 12be0f73db32b..d4d7258ff3ea1 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs @@ -94,8 +94,7 @@ private static ProjectInfo CreateProjectInfo(string projectName, string language DocumentId.CreateNewId(projectId), name: Path.GetFileNameWithoutExtension(path), loader: new WorkspaceFileTextLoaderNoException(services, path, defaultEncoding: null), - filePath: path, - loadTextOptions: new LoadTextOptions(checksumAlgorithm))); + filePath: path)); return ProjectInfo.Create( new ProjectInfo.ProjectAttributes( diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index d872ac914391f..e24829a95679b 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -349,8 +349,7 @@ private Document AddDocumentToProject(string filePath, string language, string p DocumentId.CreateNewId(project.Id), name: Path.GetFileName(filePath), loader: new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null), - filePath: filePath, - loadTextOptions: new LoadTextOptions(project.State.ChecksumAlgorithm)); + filePath: filePath); OnDocumentAdded(docInfo); return CurrentSolution.GetRequiredDocument(docInfo.Id); diff --git a/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs index 6b4dcc7a1397b..fc0b38c7f3b1e 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs @@ -37,8 +37,7 @@ private static Solution GetSolution(params string[] sources) DocumentInfo.Create( DocumentId.CreateNewId(pid), name: "code" + i, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From(s, encoding: null, SourceHashAlgorithms.Default), VersionStamp.Default)), - loadTextOptions: new LoadTextOptions(SourceHashAlgorithms.Default))).ToList(); + loader: TextLoader.From(TextAndVersion.Create(SourceText.From(s, encoding: null, SourceHashAlgorithms.Default), VersionStamp.Default)))).ToList(); var proj = ProjectInfo.Create(pid, VersionStamp.Default, "test", "test.dll", LanguageNames.CSharp, documents: docs, metadataReferences: new[] { TestMetadata.Net451.mscorlib }); diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index 3158edc7facec..a83f54039c58e 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -361,9 +361,9 @@ private Task CreateProjectInfoAsync(ProjectFileInfo projectFileInfo .WithStrongNameProvider(new DesktopStrongNameProvider(commandLineArgs.KeyFileSearchPaths)) .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default); - var documents = CreateDocumentInfos(projectFileInfo.Documents, projectId, commandLineArgs.Encoding, commandLineArgs.ChecksumAlgorithm); - var additionalDocuments = CreateDocumentInfos(projectFileInfo.AdditionalDocuments, projectId, commandLineArgs.Encoding, commandLineArgs.ChecksumAlgorithm); - var analyzerConfigDocuments = CreateDocumentInfos(projectFileInfo.AnalyzerConfigDocuments, projectId, commandLineArgs.Encoding, commandLineArgs.ChecksumAlgorithm); + var documents = CreateDocumentInfos(projectFileInfo.Documents, projectId, commandLineArgs.Encoding); + var additionalDocuments = CreateDocumentInfos(projectFileInfo.AdditionalDocuments, projectId, commandLineArgs.Encoding); + var analyzerConfigDocuments = CreateDocumentInfos(projectFileInfo.AnalyzerConfigDocuments, projectId, commandLineArgs.Encoding); CheckForDuplicateDocuments(documents.Concat(additionalDocuments).Concat(analyzerConfigDocuments), projectPath, projectId); var analyzerReferences = ResolveAnalyzerReferences(commandLineArgs); @@ -439,10 +439,9 @@ private IEnumerable ResolveAnalyzerReferences(CommandLineArgu return commandLineArgs.ResolveAnalyzerReferences(analyzerLoader).Distinct(AnalyzerReferencePathComparer.Instance); } - private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) + private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) { var results = ImmutableArray.CreateBuilder(); - var loadTextOptions = new LoadTextOptions(checksumAlgorithm); foreach (var info in documentFileInfos) { @@ -455,8 +454,7 @@ private ImmutableArray CreateDocumentInfos(IReadOnlyList System.EventHandler System.EventHandler override Microsoft.CodeAnalysis.FileTextLoader.CanReloadText.get -> bool override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -*REMOVED*static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders = null, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind = Microsoft.CodeAnalysis.SourceCodeKind.Regular, Microsoft.CodeAnalysis.TextLoader loader = null, string filePath = null, bool isGenerated = false) -> Microsoft.CodeAnalysis.DocumentInfo -static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders = null, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind = Microsoft.CodeAnalysis.SourceCodeKind.Regular, Microsoft.CodeAnalysis.TextLoader loader = null, string filePath = null, bool isGenerated = false, Microsoft.CodeAnalysis.LoadTextOptions? loadTextOptions = null) -> Microsoft.CodeAnalysis.DocumentInfo -static Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId id, string name, System.Collections.Generic.IEnumerable folders, Microsoft.CodeAnalysis.SourceCodeKind sourceCodeKind, Microsoft.CodeAnalysis.TextLoader loader, string filePath, bool isGenerated) -> Microsoft.CodeAnalysis.DocumentInfo static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText *REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs index 25a763e35afce..4f846b8cd017b 100644 --- a/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/AdhocWorkspace.cs @@ -130,7 +130,7 @@ public Document AddDocument(ProjectId projectId, string name, SourceText text) var id = DocumentId.CreateNewId(projectId); var loader = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create())); - return AddDocument(DocumentInfo.Create(id, name, loader: loader, loadTextOptions: new LoadTextOptions(text.ChecksumAlgorithm))); + return AddDocument(DocumentInfo.Create(id, name, loader: loader)); } /// diff --git a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs index 4d60105d2adbe..66f0e205b31fc 100644 --- a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs @@ -130,8 +130,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), - filePath: absolutePath, - loadTextOptions: loadTextOptions); + filePath: absolutePath); docs.Add(doc); } @@ -158,8 +157,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, folders: folders, sourceCodeKind: SourceCodeKind.Regular, loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), - filePath: absolutePath, - loadTextOptions: loadTextOptions); + filePath: absolutePath); additionalDocs.Add(doc); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs index 5eecf0d14cdb0..4ec1cf8065a9c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs @@ -27,8 +27,9 @@ private AdditionalDocumentState( public AdditionalDocumentState( DocumentInfo documentInfo, + LoadTextOptions loadTextOptions, HostWorkspaceServices solutionServices) - : base(documentInfo, solutionServices) + : base(documentInfo, loadTextOptions, solutionServices) { _additionalText = new AdditionalTextWithState(this); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs index c8841240f06ef..a500bce25ccff 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs @@ -30,8 +30,9 @@ private AnalyzerConfigDocumentState( public AnalyzerConfigDocumentState( DocumentInfo documentInfo, + LoadTextOptions loadTextOptions, HostWorkspaceServices solutionServices) - : base(documentInfo, solutionServices) + : base(documentInfo, loadTextOptions, solutionServices) { _analyzerConfigValueSource = CreateAnalyzerConfigValueSource(); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 42558716ba65c..0c3c7a94997f3 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -57,11 +57,6 @@ public sealed class DocumentInfo /// public TextLoader? TextLoader { get; } - /// - /// Options used to load . - /// - public LoadTextOptions LoadTextOptions { get; } - /// /// A associated with this document /// @@ -70,28 +65,16 @@ public sealed class DocumentInfo /// /// Create a new instance of a . /// - internal DocumentInfo(DocumentAttributes attributes, TextLoader? loader, LoadTextOptions loadTextOptions, IDocumentServiceProvider? documentServiceProvider) + internal DocumentInfo(DocumentAttributes attributes, TextLoader? loader, IDocumentServiceProvider? documentServiceProvider) { Attributes = attributes; TextLoader = loader; - LoadTextOptions = loadTextOptions; DocumentServiceProvider = documentServiceProvider; } /// /// Creates info. /// - [Obsolete] - public static DocumentInfo Create( - DocumentId id, - string name, - IEnumerable? folders, - SourceCodeKind sourceCodeKind, - TextLoader? loader, - string? filePath, - bool isGenerated) - => Create(id, name, folders, sourceCodeKind, loader, filePath, isGenerated, loadTextOptions: null); - public static DocumentInfo Create( DocumentId id, string name, @@ -99,8 +82,7 @@ public static DocumentInfo Create( SourceCodeKind sourceCodeKind = SourceCodeKind.Regular, TextLoader? loader = null, string? filePath = null, - bool isGenerated = false, - LoadTextOptions? loadTextOptions = null) + bool isGenerated = false) { return new DocumentInfo( new DocumentAttributes( @@ -112,30 +94,26 @@ public static DocumentInfo Create( isGenerated, designTimeOnly: false), loader, - loadTextOptions ?? new LoadTextOptions(SourceHashAlgorithms.Default), documentServiceProvider: null); } private DocumentInfo With( DocumentAttributes? attributes = null, Optional loader = default, - Optional loadTextOptions = default, Optional documentServiceProvider = default) { var newAttributes = attributes ?? Attributes; var newLoader = loader.HasValue ? loader.Value : TextLoader; - var newLoadTextOptions = loadTextOptions.HasValue ? loadTextOptions.Value : LoadTextOptions; var newDocumentServiceProvider = documentServiceProvider.HasValue ? documentServiceProvider.Value : DocumentServiceProvider; if (newAttributes == Attributes && newLoader == TextLoader && - newLoadTextOptions == LoadTextOptions && newDocumentServiceProvider == DocumentServiceProvider) { return this; } - return new DocumentInfo(newAttributes, newLoader, newLoadTextOptions, newDocumentServiceProvider); + return new DocumentInfo(newAttributes, newLoader, newDocumentServiceProvider); } public DocumentInfo WithId(DocumentId id) @@ -156,9 +134,6 @@ public DocumentInfo WithFilePath(string? filePath) public DocumentInfo WithTextLoader(TextLoader? loader) => With(loader: loader); - public DocumentInfo WithLoadTextOptions(LoadTextOptions options) - => With(loadTextOptions: options); - internal DocumentInfo WithDesignTimeOnly(bool designTimeOnly) => With(attributes: Attributes.With(designTimeOnly: designTimeOnly)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index be860f8fda64e..5fa664df4da60 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -55,9 +55,10 @@ protected DocumentState( public DocumentState( DocumentInfo info, ParseOptions? options, + LoadTextOptions loadTextOptions, HostLanguageServices languageServices, HostWorkspaceServices services) - : base(info, services) + : base(info, loadTextOptions, services) { _languageServices = languageServices; _options = options; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index dbe71043c6e23..7a3bb7f0b551c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -105,9 +105,10 @@ public ProjectState(ProjectInfo projectInfo, HostLanguageServices languageServic _solutionServices = solutionServices; var projectInfoFixed = FixProjectInfo(projectInfo); + var loadTextOptions = new LoadTextOptions(projectInfoFixed.Attributes.ChecksumAlgorithm); // We need to compute our AnalyerConfigDocumentStates first, since we use those to produce our DocumentStates - AnalyzerConfigDocumentStates = new TextDocumentStates(projectInfoFixed.AnalyzerConfigDocuments, info => new AnalyzerConfigDocumentState(info, solutionServices)); + AnalyzerConfigDocumentStates = new TextDocumentStates(projectInfoFixed.AnalyzerConfigDocuments, info => new AnalyzerConfigDocumentState(info, loadTextOptions, solutionServices)); _lazyAnalyzerConfigOptions = ComputeAnalyzerConfigOptionsValueSource(AnalyzerConfigDocumentStates); @@ -121,8 +122,8 @@ public ProjectState(ProjectInfo projectInfo, HostLanguageServices languageServic var parseOptions = projectInfoFixed.ParseOptions; - DocumentStates = new TextDocumentStates(projectInfoFixed.Documents, info => CreateDocument(info, parseOptions)); - AdditionalDocumentStates = new TextDocumentStates(projectInfoFixed.AdditionalDocuments, info => new AdditionalDocumentState(info, solutionServices)); + DocumentStates = new TextDocumentStates(projectInfoFixed.Documents, info => CreateDocument(info, parseOptions, loadTextOptions)); + AdditionalDocumentStates = new TextDocumentStates(projectInfoFixed.AdditionalDocuments, info => new AdditionalDocumentState(info, loadTextOptions, solutionServices)); _lazyLatestDocumentVersion = new AsyncLazy(c => ComputeLatestDocumentVersionAsync(DocumentStates, AdditionalDocumentStates, c), cacheResult: true); _lazyLatestDocumentTopLevelChangeVersion = new AsyncLazy(c => ComputeLatestDocumentTopLevelChangeVersionAsync(DocumentStates, AdditionalDocumentStates, c), cacheResult: true); @@ -237,9 +238,9 @@ private static async Task ComputeLatestDocumentTopLevelChangeVersi return latestVersion; } - internal DocumentState CreateDocument(DocumentInfo documentInfo, ParseOptions? parseOptions) + internal DocumentState CreateDocument(DocumentInfo documentInfo, ParseOptions? parseOptions, LoadTextOptions loadTextOptions) { - var doc = new DocumentState(documentInfo, parseOptions, _languageServices, _solutionServices); + var doc = new DocumentState(documentInfo, parseOptions, loadTextOptions, _languageServices, _solutionServices); if (doc.SourceCodeKind != documentInfo.SourceCodeKind) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index a7b629f09ddf8..1eddff33a716a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1054,8 +1054,7 @@ private Solution AddDocumentImpl(ProjectState project, DocumentId documentId, st sourceCodeKind: GetSourceCodeKind(project), loader: TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create(), name)), filePath: filePath, - isGenerated: isGenerated, - loadTextOptions: new LoadTextOptions(project.ChecksumAlgorithm))); + isGenerated: isGenerated)); /// /// Creates a new solution instance with the project updated to include a new document with @@ -1079,8 +1078,7 @@ public Solution AddDocument(DocumentId documentId, string name, TextLoader loade name, folders, GetSourceCodeKind(project), - loader, - loadTextOptions: new LoadTextOptions(project.ChecksumAlgorithm))); + loader)); } /// @@ -1192,8 +1190,7 @@ private DocumentInfo CreateDocumentInfo(DocumentId documentId, string name, Sour folders: folders, sourceCodeKind: GetSourceCodeKind(project), loader: loader, - filePath: filePath, - loadTextOptions: new LoadTextOptions(text.ChecksumAlgorithm)); + filePath: filePath); } private ProjectState GetRequiredProjectState(ProjectId projectId) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 9f2abca5ea1e9..abf154f99a026 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -717,7 +717,7 @@ public SolutionState WithProjectChecksumAlgorithm(ProjectId projectId, SourceHas return this; } - return ForkProject(newProject); + return ForkProject(newProject, new CompilationAndGeneratorDriverTranslationAction.ReplaceAllSyntaxTreesAction(newProject, isParseOptionChange: false)); } /// @@ -1062,7 +1062,7 @@ public SolutionState WithProjectAnalyzerReferences(ProjectId projectId, IEnumera public SolutionState AddDocuments(ImmutableArray documentInfos) { return AddDocumentsToMultipleProjects(documentInfos, - (documentInfo, project) => project.CreateDocument(documentInfo, project.ParseOptions), + (documentInfo, project) => project.CreateDocument(documentInfo, project.ParseOptions, new LoadTextOptions(project.ChecksumAlgorithm)), (oldProject, documents) => (oldProject.AddDocuments(documents), new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction(documents))); } @@ -1121,7 +1121,7 @@ private SolutionState AddDocumentsToMultipleProjects( public SolutionState AddAdditionalDocuments(ImmutableArray documentInfos) { return AddDocumentsToMultipleProjects(documentInfos, - (documentInfo, project) => new AdditionalDocumentState(documentInfo, Services), + (documentInfo, project) => new AdditionalDocumentState(documentInfo, new LoadTextOptions(project.ChecksumAlgorithm), Services), (projectState, documents) => (projectState.AddAdditionalDocuments(documents), new CompilationAndGeneratorDriverTranslationAction.AddAdditionalDocumentsAction(documents))); } @@ -1129,7 +1129,7 @@ public SolutionState AddAnalyzerConfigDocuments(ImmutableArray doc { // Adding a new analyzer config potentially modifies the compilation options return AddDocumentsToMultipleProjects(documentInfos, - (documentInfo, project) => new AnalyzerConfigDocumentState(documentInfo, Services), + (documentInfo, project) => new AnalyzerConfigDocumentState(documentInfo, new LoadTextOptions(project.ChecksumAlgorithm), Services), (oldProject, documents) => { var newProject = oldProject.AddAnalyzerConfigDocuments(documents); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index c34e54ca3af4e..3c03ec24bf74b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -72,15 +72,15 @@ protected TextDocumentState( _lazyChecksums = new AsyncLazy(ComputeChecksumsAsync, cacheResult: true); } - public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) + public TextDocumentState(DocumentInfo info, LoadTextOptions loadTextOptions, HostWorkspaceServices services) : this(services, info.DocumentServiceProvider, info.Attributes, sourceText: null, textAndVersionSource: info.TextLoader != null ? CreateRecoverableText(info.TextLoader, services.SolutionServices) - : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, encoding: null, info.LoadTextOptions.ChecksumAlgorithm), VersionStamp.Default, info.FilePath)), - info.LoadTextOptions) + : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, encoding: null, loadTextOptions.ChecksumAlgorithm), VersionStamp.Default, info.FilePath)), + loadTextOptions) { } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index f07c6c7146d39..0e93969f5fd3e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -1629,7 +1629,7 @@ private void ApplyChangedDocument( // ApplyDocumentInfoChanged ignores the loader information, so we can pass null for it ApplyDocumentInfoChanged( documentId, - new DocumentInfo(newDoc.State.Attributes, loader: null, newDoc.State.LoadTextOptions, documentServiceProvider: newDoc.State.Services)); + new DocumentInfo(newDoc.State.Attributes, loader: null, documentServiceProvider: newDoc.State.Services)); } } @@ -1668,8 +1668,7 @@ internal static DocumentInfo CreateDocumentInfoWithoutText(TextDocument doc) doc is Document sourceDoc ? sourceDoc.SourceCodeKind : SourceCodeKind.Regular, loader: null, filePath: doc.FilePath, - isGenerated: doc.State.Attributes.IsGenerated, - loadTextOptions: doc.State.LoadTextOptions) + isGenerated: doc.State.Attributes.IsGenerated) .WithDesignTimeOnly(doc.State.Attributes.DesignTimeOnly) .WithDocumentServiceProvider(doc.Services); diff --git a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs index 4a4fbed5e38e4..c63826c4f6c14 100644 --- a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs @@ -47,7 +47,6 @@ public void Create() Assert.Equal(SourceCodeKind.Script, info.SourceCodeKind); Assert.Same(loader, info.TextLoader); Assert.True(info.IsGenerated); - Assert.Equal(SourceHashAlgorithms.Default, info.LoadTextOptions.ChecksumAlgorithm); } [Fact] diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index b006552b8e428..11331ea45542b 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -92,7 +92,7 @@ public async Task CreateDocumentInfoAsync(Checksum documentChecksu var textLoader = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create(), attributes.FilePath)); // TODO: do we need version? - return new DocumentInfo(attributes, textLoader, new LoadTextOptions(text.ChecksumAlgorithm), documentServiceProvider: null); + return new DocumentInfo(attributes, textLoader, documentServiceProvider: null); } private async Task> CreateDocumentInfosAsync(ChecksumCollection documentChecksums, CancellationToken cancellationToken) From 7c55bb2f4305b74d4a666a22eb5c21854b1a2860 Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 13:29:47 -0700 Subject: [PATCH 21/32] Test --- docs/Breaking API Changes.md | 2 +- .../Workspace/Solution/ProjectState.cs | 1 - .../CoreTest/SolutionTests/SolutionTests.cs | 44 +++++++++++-------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/docs/Breaking API Changes.md b/docs/Breaking API Changes.md index 72b8a95c02c85..994d2b6dd8d3a 100644 --- a/docs/Breaking API Changes.md +++ b/docs/Breaking API Changes.md @@ -70,4 +70,4 @@ All property setters now throw an exception. `Workspace.OnWorkspaceFailed` is no longer called when an error occurs while reading source file content from disk. The `Workspace` and `DocumentId` parameters of `TextLoader.LoadTextAndVersionAsync(Workspace, DocumentId, CancellationToken)` are deprecated. -The method now receives an instance of an immutable empty `Workspace` with default workspace services, and a fake `DocumentId`. +The method now receives `null` `Workspace` and `DocumentId`. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 7a3bb7f0b551c..8ba470c40d90c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 167fc41d8db66..65221af38bbfd 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -653,7 +653,7 @@ public void WithProjectChecksumAlgorithm() } [Fact] - public void WithProjectChecksumAlgorithm_DocumentUpdates() + public async Task WithProjectChecksumAlgorithm_DocumentUpdates() { var projectId = ProjectId.CreateNewId(); var documentAId = DocumentId.CreateNewId(projectId); @@ -681,39 +681,47 @@ public void WithProjectChecksumAlgorithm_DocumentUpdates() .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp) .WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); - solution = solution.AddDocument(documentAId, "a.cs", textLoaderA); - solution = solution.AddDocument(documentBId, "b.cs", "class B {}"); - solution = solution.AddDocument(documentCId, "c.cs", textC); - solution = solution.AddDocument(fileDocumentId, "d.cs", new FileTextLoader(fileD.Path, defaultEncoding: null)); + solution = solution.AddDocument(DocumentInfo.Create(documentAId, "a.cs", loader: textLoaderA, filePath: "a.cs")); + solution = solution.AddDocument(documentBId, "b.cs", "class B {}", filePath: "b.cs"); + solution = solution.AddDocument(documentCId, "c.cs", textC, filePath: "c.cs"); + solution = solution.AddDocument(DocumentInfo.Create(fileDocumentId, "d.cs", loader: new FileTextLoader(fileD.Path, defaultEncoding: null), filePath: fileD.Path)); - Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha1, checksumSHA1); + await Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha1, checksumSHA1); // only file loader based documents support updating checksum alg: solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha256); - Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha256, checksumSHA256); + await Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha256, checksumSHA256); // only file loader based documents support updating checksum alg: solution = solution.WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); - Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha1, checksumSHA1); + await Verify(solution.GetRequiredDocument(documentAId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(documentBId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(documentCId), SourceHashAlgorithm.Sha1); + await Verify(solution.GetRequiredDocument(fileDocumentId), SourceHashAlgorithm.Sha1, checksumSHA1); - static void Verify(Document document, SourceHashAlgorithm expectedAlgorithm, byte[]? expectedChecksum = null) + static async Task Verify(Document document, SourceHashAlgorithm expectedAlgorithm, byte[]? expectedChecksum = null) { Assert.Equal(expectedAlgorithm, document.State.LoadTextOptions.ChecksumAlgorithm); + Assert.Equal(expectedAlgorithm, (await document.GetTextAsync(default)).ChecksumAlgorithm); Assert.Equal(expectedAlgorithm, document.GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(expectedAlgorithm, (await document.GetRequiredSyntaxTreeAsync(default)).GetText().ChecksumAlgorithm); + Assert.Equal(expectedAlgorithm, document.GetRequiredSyntaxTreeSynchronously(default).GetText().ChecksumAlgorithm); if (expectedChecksum != null) { Assert.Equal(expectedChecksum, document.GetTextSynchronously(default).GetChecksum()); } + + var compilation = await document.Project.GetRequiredCompilationAsync(default); + var tree = compilation.SyntaxTrees.Single(t => t.FilePath == document.FilePath); + Assert.Equal(expectedAlgorithm, (await tree.GetTextAsync(default)).ChecksumAlgorithm); + Assert.Equal(expectedAlgorithm, tree.GetText(default).ChecksumAlgorithm); } } From 0f0b972215dd0659065ef34977974b5c1f77867e Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 13:50:05 -0700 Subject: [PATCH 22/32] Make CanReloadText internal --- src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt | 2 -- .../Core/Portable/Workspace/Solution/FileTextLoader.cs | 2 +- src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 1747606973b2c..2459495951644 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -35,12 +35,10 @@ Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentClosedEventAsync(Microsoft.Cod Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentOpenedEventAsync(Microsoft.CodeAnalysis.TextDocument document) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Workspace.TextDocumentClosed -> System.EventHandler Microsoft.CodeAnalysis.Workspace.TextDocumentOpened -> System.EventHandler -override Microsoft.CodeAnalysis.FileTextLoader.CanReloadText.get -> bool override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText *REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task *REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.TextLoader.CanReloadText.get -> bool virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs index 4455d69a2efba..3849d4c030183 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs @@ -62,7 +62,7 @@ public FileTextLoader(string path, Encoding? defaultEncoding) internal sealed override string FilePath => Path; - public override bool CanReloadText => !IsObsoleteCreateTextOverridden; + internal override bool CanReloadText => !IsObsoleteCreateTextOverridden; #pragma warning disable CS0618 // Type or member is obsolete private bool IsObsoleteCreateTextOverridden diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index cfac32acb7522..e9880795ee0ca 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -36,7 +36,7 @@ public abstract class TextLoader /// /// True if reloads from its original binary representation (e.g. file on disk). /// - public virtual bool CanReloadText + internal virtual bool CanReloadText => false; /// From df2c9e13e3be1b214e004bfa0cab0773b5df746c Mon Sep 17 00:00:00 2001 From: tmat Date: Mon, 26 Sep 2022 14:32:44 -0700 Subject: [PATCH 23/32] Test --- .../CPS/AdditionalPropertiesTests.cs | 1 + .../CPS/CSharpCompilerOptionsTests.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/AdditionalPropertiesTests.cs b/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/AdditionalPropertiesTests.cs index 7acb5b5f5fc11..04e6898e7d885 100644 --- a/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/AdditionalPropertiesTests.cs +++ b/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/AdditionalPropertiesTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio; using Microsoft.VisualStudio.LanguageServices.CSharp.Utilities; using Microsoft.VisualStudio.LanguageServices.ProjectSystem; diff --git a/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpCompilerOptionsTests.cs b/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpCompilerOptionsTests.cs index 4f08c92d5f224..b5275bdb4356e 100644 --- a/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpCompilerOptionsTests.cs +++ b/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpCompilerOptionsTests.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework; using Roslyn.Test.Utilities; @@ -166,5 +167,19 @@ public async Task ProjectFilePathSetter_CPS() SharedResourceHelpers.CleanupAllGeneratedFiles(newFilePath); } + + [WpfFact] + public async Task ChecksumAlgorithm_CPS() + { + using var environment = new TestEnvironment(); + using var cpsProject = await CSharpHelpers.CreateCSharpCPSProjectAsync(environment, "Test"); + + Assert.Equal(SourceHashAlgorithms.Default, environment.Workspace.CurrentSolution.Projects.Single().State.ChecksumAlgorithm); + + cpsProject.SetOptions(ImmutableArray.Create("/checksumalgorithm:SHA1")); + + var project = environment.Workspace.CurrentSolution.Projects.Single(); + Assert.Equal(SourceHashAlgorithm.Sha1, environment.Workspace.CurrentSolution.Projects.Single().State.ChecksumAlgorithm); + } } } From 4b25517acd608d6556270d32b4fce6093250bc94 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 20 Jul 2022 11:17:28 -0700 Subject: [PATCH 24/32] EnC: use the document snapshot when validating PDB checksum --- ...itAndContinueHostWorkspaceEventListener.cs | 47 ++++++-- .../EditAndContinueWorkspaceServiceTests.cs | 6 +- .../EditAndContinue/CommittedSolution.cs | 112 ++++++++++++------ .../Workspace/Solution/TextDocumentState.cs | 2 +- 4 files changed, 120 insertions(+), 47 deletions(-) diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs index a0b961bbc8ead..cb60e1ba2a151 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -27,29 +28,55 @@ public EditAndContinueHostWorkspaceEventListener() public void StartListening(Workspace workspace, object serviceOpt) { - workspace.DocumentOpened += WorkspaceDocumentOpened; + workspace.WorkspaceChanged += WorkspaceChanged; } - public void StopListening(Workspace workspace) + private static void WorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) { - workspace.DocumentOpened -= WorkspaceDocumentOpened; - } + Debug.Assert(sender is Workspace); - private void WorkspaceDocumentOpened(object? sender, DocumentEventArgs e) - { if (!DebuggerContractVersionCheck.IsRequiredDebuggerContractVersionAvailable()) { return; } - WorkspaceDocumentOpenedImpl(e); + if (e.DocumentId == null) + { + return; + } + + var oldDocument = e.OldSolution.GetDocument(e.DocumentId); + if (oldDocument == null) + { + // document added + return; + } + + var newDocument = e.NewSolution.GetDocument(e.DocumentId); + if (newDocument == null) + { + // document removed + return; + } + + // When a document is open its loader transitions from file-based loader to text buffer based. + // The file checksum is no longer available from the latter, so capture it at this moment. + if (oldDocument.State.TextAndVersionSource.CanReloadText && !newDocument.State.TextAndVersionSource.CanReloadText) + { + WorkspaceDocumentLoaderChanged((Workspace)sender, oldDocument); + } + } + + public void StopListening(Workspace workspace) + { + workspace.WorkspaceChanged -= WorkspaceChanged; } [MethodImpl(MethodImplOptions.NoInlining)] - private static void WorkspaceDocumentOpenedImpl(DocumentEventArgs e) + private static void WorkspaceDocumentLoaderChanged(Workspace workspace, Document document) { - var proxy = new RemoteEditAndContinueServiceProxy(e.Document.Project.Solution.Workspace); - _ = Task.Run(() => proxy.OnSourceFileUpdatedAsync(e.Document, CancellationToken.None)).ReportNonFatalErrorAsync(); + var proxy = new RemoteEditAndContinueServiceProxy(workspace); + _ = Task.Run(() => proxy.OnSourceFileUpdatedAsync(document, CancellationToken.None)).ReportNonFatalErrorAsync(); } } } diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 9e80a1286db65..e5ebcce0ea8da 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -492,7 +492,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume // prepare workspace as if it was loaded from project files: using var _ = CreateWorkspace(out var solution, out var service, new[] { typeof(NoCompilationLanguageService) }); - var projectPId = ProjectId.CreateNewId(); + var projectPId = ProjectId.CreateNewId(); solution = solution .AddProject(projectPId, "P", "P", LanguageNames.CSharp) .WithProjectChecksumAlgorithm(projectPId, SourceHashAlgorithm.Sha1); @@ -984,7 +984,7 @@ public async Task ErrorReadingSourceFile() var document1 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(DefaultTargetFramework)). - AddDocument("a.cs", SourceText.From(source1, Encoding.UTF8), filePath: sourceFile.Path); + AddDocument("a.cs", SourceText.From(source1, Encoding.UTF8, SourceHashAlgorithms.Default), filePath: sourceFile.Path); var project = document1.Project; solution = project.Solution; @@ -995,7 +995,7 @@ public async Task ErrorReadingSourceFile() EnterBreakState(debuggingSession); // change the source: - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8, SourceHashAlgorithms.Default)); var document2 = solution.GetDocument(document1.Id); using var fileLock = File.Open(sourceFile.Path, FileMode.Open, FileAccess.Read, FileShare.None); diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index 2230f13e062b6..a12e6b30080de 100644 --- a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs +++ b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text; @@ -229,24 +230,12 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel return (null, DocumentState.DesignTimeOnly); } + Contract.ThrowIfNull(document.FilePath); + var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var sourceTextVersion = (committedDocument == null) ? await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false) : default; - // run file IO on a background thread: - var (matchingSourceText, pdbHasDocument) = await Task.Run(() => - { - var compilationOutputs = _debuggingSession.GetCompilationOutputs(document.Project); - using var debugInfoReaderProvider = GetMethodDebugInfoReader(compilationOutputs, document.Project.Name); - if (debugInfoReaderProvider == null) - { - return (null, null); - } - - var debugInfoReader = debugInfoReaderProvider.CreateEditAndContinueMethodDebugInfoReader(); - - Contract.ThrowIfNull(document.FilePath); - return TryGetPdbMatchingSourceText(debugInfoReader, document.FilePath, sourceText.Encoding); - }, cancellationToken).ConfigureAwait(false); + var (maybeMatchingSourceText, maybePdbHasDocument) = await TryGetMatchingSourceTextAsync(document, sourceText, currentDocument, cancellationToken).ConfigureAwait(false); lock (_guard) { @@ -261,13 +250,13 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel DocumentState newState; Document? matchingDocument; - if (pdbHasDocument == null) + if (!maybePdbHasDocument.HasValue) { - // Unable to determine due to error reading the PDB or the source file. + // Unable to determine due to error reading the PDB. return (document, DocumentState.Indeterminate); } - if (pdbHasDocument == false) + if (!maybePdbHasDocument.Value) { // Source file is not listed in the PDB. // It could either be a newly added document or a design-time-only document (e.g. WPF .g.i.cs files). @@ -275,6 +264,11 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel matchingDocument = null; newState = (committedDocument != null) ? DocumentState.DesignTimeOnly : DocumentState.MatchesBuildOutput; } + else if (!maybeMatchingSourceText.HasValue) + { + // Unable to determine due to error reading the source file. + return (document, DocumentState.Indeterminate); + } else { // Document exists in the PDB but not in the committed solution. @@ -297,6 +291,7 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel .WithDocumentServiceProvider(document.State.Services)); } + var matchingSourceText = maybeMatchingSourceText.Value; if (matchingSourceText != null) { if (committedDocument != null && sourceText.ContentEquals(matchingSourceText)) @@ -323,6 +318,38 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel } } + private async ValueTask<(Optional matchingSourceText, bool? hasDocument)> TryGetMatchingSourceTextAsync(Document document, SourceText sourceText, Document? currentDocument, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(document.FilePath); + + var maybePdbHasDocument = TryReadSourceFileChecksumFromPdb(document, out var requiredChecksum, out var checksumAlgorithm); + + var maybeMatchingSourceText = (maybePdbHasDocument == true) ? + await TryGetMatchingSourceTextAsync(sourceText, document.FilePath, currentDocument, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) : default; + + return (maybeMatchingSourceText, maybePdbHasDocument); + } + + private static async ValueTask> TryGetMatchingSourceTextAsync( + SourceText sourceText, string filePath, Document? currentDocument, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + { + if (IsMatchingSourceText(sourceText, requiredChecksum, checksumAlgorithm)) + { + return sourceText; + } + + if (currentDocument != null) + { + var currentDocumentSourceText = await currentDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + if (IsMatchingSourceText(currentDocumentSourceText, requiredChecksum, checksumAlgorithm)) + { + return currentDocumentSourceText; + } + } + + return await Task.Run(() => TryGetPdbMatchingSourceTextFromDisk(filePath, sourceText.Encoding, requiredChecksum, checksumAlgorithm), cancellationToken).ConfigureAwait(false); + } + internal static async Task>> GetMatchingDocumentsAsync( IEnumerable<(Project, IEnumerable)> documentsByProject, Func compilationOutputsProvider, @@ -364,8 +391,8 @@ internal static async Task>> // TODO: https://github.com/dotnet/roslyn/issues/51993 // avoid rereading the file in common case - the workspace should create source texts with the right checksum algorithm and encoding - var (source, hasDocument) = TryGetPdbMatchingSourceText(debugInfoReader, sourceFilePath, sourceText.Encoding); - if (source != null) + if (TryReadSourceFileChecksumFromPdb(debugInfoReader, sourceFilePath, out var requiredChecksum, out var checksumAlgorithm) == true && + await TryGetMatchingSourceTextAsync(sourceText, sourceFilePath, currentDocument: null, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) is { HasValue: true, Value: not null }) { return documentState.Id; } @@ -411,14 +438,11 @@ public void CommitSolution(Solution solution) } } - private static (SourceText? Source, bool? HasDocument) TryGetPdbMatchingSourceText(EditAndContinueMethodDebugInfoReader debugInfoReader, string sourceFilePath, Encoding? encoding) - { - var hasDocument = TryReadSourceFileChecksumFromPdb(debugInfoReader, sourceFilePath, out var symChecksum, out var algorithm); - if (hasDocument != true) - { - return (Source: null, hasDocument); - } + private static bool IsMatchingSourceText(SourceText sourceText, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm) + => checksumAlgorithm == sourceText.ChecksumAlgorithm && sourceText.GetChecksum().SequenceEqual(requiredChecksum); + private static Optional TryGetPdbMatchingSourceTextFromDisk(string sourceFilePath, Encoding? encoding, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm) + { try { using var fileStream = new FileStream(sourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete); @@ -427,22 +451,43 @@ private static (SourceText? Source, bool? HasDocument) TryGetPdbMatchingSourceTe // This might differ from the encoding that the compiler chooses, so if we just relied on the compiler we // might end up updating the committed solution with a document that has a different encoding than // the one that's in the workspace, resulting in false document changes when we compare the two. - var sourceText = SourceText.From(fileStream, encoding, checksumAlgorithm: algorithm); - var fileChecksum = sourceText.GetChecksum(); + var sourceText = SourceText.From(fileStream, encoding, checksumAlgorithm); - if (fileChecksum.SequenceEqual(symChecksum)) + if (IsMatchingSourceText(sourceText, requiredChecksum, checksumAlgorithm)) { - return (sourceText, hasDocument); + return sourceText; } EditAndContinueWorkspaceService.Log.Write("Checksum differs for source file '{0}'", sourceFilePath); - return (Source: null, hasDocument); + + // does not match: + return null; } catch (Exception e) { EditAndContinueWorkspaceService.Log.Write("Error calculating checksum for source file '{0}': '{1}'", sourceFilePath, e.Message); - return (Source: null, HasDocument: null); + + // unable to determine: + return default; + } + } + + private bool? TryReadSourceFileChecksumFromPdb(Document document, out ImmutableArray requiredChecksum, out SourceHashAlgorithm checksumAlgorithm) + { + Contract.ThrowIfNull(document.FilePath); + + var compilationOutputs = _debuggingSession.GetCompilationOutputs(document.Project); + using var debugInfoReaderProvider = GetMethodDebugInfoReader(compilationOutputs, document.Project.Name); + if (debugInfoReaderProvider == null) + { + // unable to determine whether document is in the PDB + requiredChecksum = default; + checksumAlgorithm = default; + return null; } + + var debugInfoReader = debugInfoReaderProvider.CreateEditAndContinueMethodDebugInfoReader(); + return TryReadSourceFileChecksumFromPdb(debugInfoReader, document.FilePath, out requiredChecksum, out checksumAlgorithm); } /// @@ -478,6 +523,7 @@ private static (SourceText? Source, bool? HasDocument) TryGetPdbMatchingSourceTe EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match output PDB: error reading symbols: {1}", sourceFilePath, e.Message); } + // unable to determine return null; } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index 3c03ec24bf74b..cf52f87a803f4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -34,7 +34,7 @@ internal partial class TextDocumentState /// only retrieve is asynchronously through . /// protected readonly SourceText? sourceText; - protected ITextAndVersionSource TextAndVersionSource { get; } + internal ITextAndVersionSource TextAndVersionSource { get; } public readonly LoadTextOptions LoadTextOptions; // Checksums for this solution state From 3cc507a84a84fea221ee1ed96d03ed6d9bb586d1 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 28 Sep 2022 09:42:16 -0700 Subject: [PATCH 25/32] Fix style --- .../Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index 732ccdf2b76d4..5b3e6041b56b7 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -355,7 +355,6 @@ private async Task UpdateProjectInfoAsync(Project project, Checksum inf return project; } - private async Task UpdateDocumentsAsync( Project project, ProjectStateChecksums projectChecksums, From 54005afdff05945094f8d7491686e103b58629f5 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 28 Sep 2022 09:42:48 -0700 Subject: [PATCH 26/32] Make new TextLoader APIs internal --- src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs | 1 + .../EditAndContinueWorkspaceServiceTests.cs | 2 +- .../TestUtilities/Workspaces/TestHostDocument.cs | 2 +- .../Workspaces/LspMiscellaneousFilesWorkspace.cs | 2 +- .../Preview/PreviewUpdater.PreviewDialogWorkspace.cs | 2 +- .../VisualStudioProject.BatchingDocumentCollection.cs | 2 +- .../Projects/WorkspaceFileTextLoaderNoException.cs | 2 +- src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt | 5 +---- src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt | 11 ----------- .../Portable/Workspace/Solution/FileTextLoader.cs | 8 +++++--- .../Core/Portable/Workspace/Solution/TextLoader.cs | 9 ++++----- .../Portable/Workspace/WorkspaceFileTextLoader.cs | 2 +- .../Core/Portable/Workspace/Workspace_Editor.cs | 2 +- .../CoreTest/SolutionTests/TextLoaderTests.cs | 4 +--- src/Workspaces/CoreTestUtilities/TestTextLoader.cs | 2 +- 15 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index 6c9b5f5db1d50..64036075b1d79 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1548,6 +1548,7 @@ public static SyntaxTree SyntaxTree(SyntaxNode root, ParseOptions? options = nul #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters #pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. +#pragma warning disable RS0030 // Do not used banned APIs #pragma warning disable CS0618 // Type or member is obsolete /// public static SyntaxTree ParseSyntaxTree( diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index e5ebcce0ea8da..e1b0b1a7ce5d0 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -375,7 +375,7 @@ private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, st internal sealed class FailingTextLoader : TextLoader { - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { Assert.True(false, $"Content of document should never be loaded"); throw ExceptionUtilities.Unreachable; diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index c719b70842226..0fcd99de79fc3 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -213,7 +213,7 @@ internal TestDocumentLoader(TestHostDocument hostDocument, string text) internal override string? FilePath => _hostDocument.FilePath; - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(SourceText.From(_text, encoding: null, options.ChecksumAlgorithm), VersionStamp.Create(), _hostDocument.FilePath)); } diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 2eaf8d26d5d39..41d2c7bda8ca6 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -107,7 +107,7 @@ internal override string? FilePath => _fileUri; // TODO (https://github.com/dotnet/roslyn/issues/63583): Use options.ChecksumAlgorithm - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_sourceText, VersionStamp.Create(), _fileUri)); } } diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index 89053a3ac4f35..9f20dbb1c882b 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs @@ -50,7 +50,7 @@ private sealed class PreviewTextLoader : TextLoader internal PreviewTextLoader(SourceText documentText) => _text = documentText; - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(LoadTextAndVersionSynchronously(options, cancellationToken)); internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index a136b5f783f13..c934f72bab07f 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -604,7 +604,7 @@ public SourceTextLoader(SourceTextContainer textContainer, string? filePath) _filePath = filePath; } - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_textContainer.CurrentText, VersionStamp.Create(), _filePath)); } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs index 03de8a968935a..13006ee243fd0 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs @@ -25,7 +25,7 @@ public WorkspaceFileTextLoaderNoException(SolutionServices services, string path { } - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { if (!File.Exists(Path)) { diff --git a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt index 791ccc8106fdb..8b137891791fe 100644 --- a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt @@ -1,4 +1 @@ -virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) -override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) -*REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) -*REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) + diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 2459495951644..481fc82e97e8e 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,3 @@ -Microsoft.CodeAnalysis.DocumentInfo.LoadTextOptions.get -> Microsoft.CodeAnalysis.LoadTextOptions -Microsoft.CodeAnalysis.DocumentInfo.WithLoadTextOptions(Microsoft.CodeAnalysis.LoadTextOptions options) -> Microsoft.CodeAnalysis.DocumentInfo Microsoft.CodeAnalysis.Editing.DeclarationModifiers.IsFile.get -> bool Microsoft.CodeAnalysis.Editing.DeclarationModifiers.WithIsFile(bool isFile) -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers *REMOVED*static Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions @@ -21,11 +19,6 @@ Microsoft.CodeAnalysis.ISupportedChangesService Microsoft.CodeAnalysis.ISupportedChangesService.CanApplyChange(Microsoft.CodeAnalysis.ApplyChangesKind kind) -> bool Microsoft.CodeAnalysis.ISupportedChangesService.CanApplyCompilationOptionChange(Microsoft.CodeAnalysis.CompilationOptions oldOptions, Microsoft.CodeAnalysis.CompilationOptions newOptions, Microsoft.CodeAnalysis.Project project) -> bool Microsoft.CodeAnalysis.ISupportedChangesService.CanApplyParseOptionChange(Microsoft.CodeAnalysis.ParseOptions oldOptions, Microsoft.CodeAnalysis.ParseOptions newOptions, Microsoft.CodeAnalysis.Project project) -> bool -Microsoft.CodeAnalysis.LoadTextOptions -Microsoft.CodeAnalysis.LoadTextOptions.ChecksumAlgorithm.get -> Microsoft.CodeAnalysis.Text.SourceHashAlgorithm -Microsoft.CodeAnalysis.LoadTextOptions.ChecksumAlgorithm.init -> void -Microsoft.CodeAnalysis.LoadTextOptions.LoadTextOptions() -> void -Microsoft.CodeAnalysis.LoadTextOptions.LoadTextOptions(Microsoft.CodeAnalysis.Text.SourceHashAlgorithm ChecksumAlgorithm) -> void Microsoft.CodeAnalysis.Project.Services.get -> Microsoft.CodeAnalysis.Host.LanguageServices Microsoft.CodeAnalysis.Solution.Services.get -> Microsoft.CodeAnalysis.Host.SolutionServices Microsoft.CodeAnalysis.TextDocumentEventArgs @@ -35,10 +28,6 @@ Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentClosedEventAsync(Microsoft.Cod Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentOpenedEventAsync(Microsoft.CodeAnalysis.TextDocument document) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Workspace.TextDocumentClosed -> System.EventHandler Microsoft.CodeAnalysis.Workspace.TextDocumentOpened -> System.EventHandler -override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers -virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText -*REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task *REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.LoadTextOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs index 3849d4c030183..827d7084b39e1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs @@ -75,7 +75,6 @@ private bool IsObsoleteCreateTextOverridden /// /// Stream. /// Obsolete. Null. - [Obsolete("Use CreateText(Stream, CancellationToken)")] protected virtual SourceText CreateText(Stream stream, Workspace? workspace) => EncodedStringText.Create(stream, DefaultEncoding, checksumAlgorithm: SourceHashAlgorithms.Default); @@ -83,18 +82,21 @@ protected virtual SourceText CreateText(Stream stream, Workspace? workspace) /// Creates from . /// #pragma warning disable CS0618 // Type or member is obsolete - protected virtual SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) + private protected virtual SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) => IsObsoleteCreateTextOverridden ? CreateText(stream, workspace: null) : EncodedStringText.Create(stream, DefaultEncoding, checksumAlgorithm: options.ChecksumAlgorithm); #pragma warning restore + public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => base.LoadTextAndVersionAsync(workspace, documentId, cancellationToken); + /// /// Load a text and a version of the document in the workspace. /// /// /// - public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { ValidateFileLength(Path); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index e9880795ee0ca..14d06653146b5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis { - public readonly record struct LoadTextOptions(SourceHashAlgorithm ChecksumAlgorithm); + internal readonly record struct LoadTextOptions(SourceHashAlgorithm ChecksumAlgorithm); /// /// A class that represents access to a source text and its version from a storage location. @@ -50,7 +50,7 @@ internal virtual bool CanReloadText /// /// /// - public virtual Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal virtual Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { #pragma warning disable CS0618 // Type or member is obsolete if (s_isObsoleteLoadTextAndVersionAsyncOverriden.GetValue( @@ -72,7 +72,6 @@ public virtual Task LoadTextAndVersionAsync(LoadTextOptions opti /// /// /// - [Obsolete("Use LoadTextAndVersionAsync(CancellationToken) instead")] public virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) => LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), cancellationToken); @@ -208,7 +207,7 @@ private sealed class TextDocumentLoader : TextLoader internal TextDocumentLoader(TextAndVersion textAndVersion) => _textAndVersion = textAndVersion; - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(_textAndVersion); internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) @@ -231,7 +230,7 @@ internal TextContainerLoader(SourceTextContainer container, VersionStamp version internal override string? FilePath => _filePath; - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(LoadTextAndVersionSynchronously(options, cancellationToken)); internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs index 10a96bb39170d..5322199a4d3ad 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs @@ -27,7 +27,7 @@ internal WorkspaceFileTextLoader(SolutionServices services, string path, Encodin _textFactory = services.GetRequiredService(); } - protected override SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) + private protected override SourceText CreateText(Stream stream, LoadTextOptions options, CancellationToken cancellationToken) => _textFactory.CreateText(stream, DefaultEncoding, options.ChecksumAlgorithm, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 1af118d0cd740..bcd6e1c82e55b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -477,7 +477,7 @@ public ReuseVersionLoader(DocumentState oldDocumentState, SourceText newText) internal override string? FilePath => _oldDocumentState.FilePath; - public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { var oldText = await _oldDocumentState.GetTextAsync(cancellationToken).ConfigureAwait(false); var version = await _oldDocumentState.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs index 5df444234c8fc..eca670e8b2c63 100644 --- a/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs @@ -81,7 +81,6 @@ private class LoaderOverridesObsolete : TextLoader { public static readonly TextAndVersion Value = TextAndVersion.Create(SourceText.From(""), VersionStamp.Default); - [Obsolete] public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) => Task.FromResult(Value); } @@ -90,7 +89,6 @@ private class LoaderOverridesObsolete2 : LoaderOverridesObsolete { public static new readonly TextAndVersion Value = TextAndVersion.Create(SourceText.From(""), VersionStamp.Default); - [Obsolete] public override Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) => Task.FromResult(Value); } @@ -99,7 +97,7 @@ private class LoaderOverridesNew : TextLoader { public static readonly TextAndVersion Value = TextAndVersion.Create(SourceText.From(""), VersionStamp.Default); - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(Value); } diff --git a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs index b21164fce03e3..7edb0a4451f27 100644 --- a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs +++ b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs @@ -20,7 +20,7 @@ public TestTextLoader(string text = "test", SourceHashAlgorithm checksumAlgorith _textAndVersion = TextAndVersion.Create(SourceText.From(text, encoding: null, checksumAlgorithm), VersionStamp.Create()); } - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(_textAndVersion); } } From 83753d7395605233d3042faed09c51ae460d2167 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 28 Sep 2022 09:54:36 -0700 Subject: [PATCH 27/32] Feedback --- .../Test/Syntax/Syntax/SyntaxTreeTests.cs | 20 ++++++++++-------- .../Test/Syntax/Syntax/SyntaxTreeTests.vb | 21 +++++++++++-------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs index 9ea7328fb2aed..7c22fd842e7d5 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs @@ -123,7 +123,8 @@ public void Create() } // Diagnostic options on syntax trees are now obsolete - [Fact, Obsolete("Testing obsolete API")] +#pragma warning disable CS0618 + [Fact] public void Create_WithDiagnosticOptions() { var options = CreateImmutableDictionary(("CS0078", ReportDiagnostic.Suppress)); @@ -133,7 +134,7 @@ public void Create_WithDiagnosticOptions() Assert.Equal(SourceHashAlgorithm.Sha1, tree.GetText().ChecksumAlgorithm); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void ParseTreeWithChangesPreservesDiagnosticOptions() { var options = CreateImmutableDictionary(("CS0078", ReportDiagnostic.Suppress)); @@ -149,7 +150,7 @@ public void ParseTreeWithChangesPreservesDiagnosticOptions() Assert.Same(options, newTree.DiagnosticOptions); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void ParseTreeNullDiagnosticOptions() { var tree = CSharpSyntaxTree.ParseText( @@ -165,7 +166,7 @@ public void ParseTreeNullDiagnosticOptions() Assert.NotSame(ImmutableDictionary.Empty, tree.DiagnosticOptions); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void ParseTreeEmptyDiagnosticOptions() { var tree = CSharpSyntaxTree.ParseText( @@ -180,7 +181,7 @@ public void ParseTreeEmptyDiagnosticOptions() Assert.Same(ImmutableDictionary.Empty, tree.DiagnosticOptions); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void ParseTreeCustomDiagnosticOptions() { var options = CreateImmutableDictionary(("CS0078", ReportDiagnostic.Suppress)); @@ -194,7 +195,7 @@ public void ParseTreeCustomDiagnosticOptions() Assert.Same(options, tree.DiagnosticOptions); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void DefaultTreeDiagnosticOptions() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -202,7 +203,7 @@ public void DefaultTreeDiagnosticOptions() Assert.True(tree.DiagnosticOptions.IsEmpty); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void WithDiagnosticOptionsNull() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -212,7 +213,7 @@ public void WithDiagnosticOptionsNull() Assert.Same(tree, newTree); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void WithDiagnosticOptionsEmpty() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -223,7 +224,7 @@ public void WithDiagnosticOptionsEmpty() Assert.NotSame(tree.DiagnosticOptions, newTree.DiagnosticOptions); } - [Fact, Obsolete("Testing obsolete API")] + [Fact] public void PerTreeDiagnosticOptionsNewDict() { var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()); @@ -234,6 +235,7 @@ public void PerTreeDiagnosticOptionsNewDict() Assert.Same(map, newTree.DiagnosticOptions); Assert.NotEqual(tree, newTree); } +#pragma warning restore CS0618 [Fact] public void WithRootAndOptions_ParsedTree() diff --git a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb index 5f48ec6ab77b4..95b80b31dc5a5 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb @@ -18,14 +18,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Equal(SourceHashAlgorithm.Sha1, tree.GetText().ChecksumAlgorithm) End Sub - + ' Diagnostic options on syntax trees are now obsolete +#Disable Warning BC40000 + Public Sub Create_WithDiagnosticOptions() Dim options = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) Dim tree = VisualBasicSyntaxTree.Create(SyntaxFactory.ParseCompilationUnit(""), options:=Nothing, path:=Nothing, encoding:=Nothing, diagnosticOptions:=options) Assert.Same(options, tree.DiagnosticOptions) End Sub - + Public Sub ParseTreeWithChangesPreservesDiagnosticOptions() Dim options = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) Dim tree = VisualBasicSyntaxTree.ParseText( @@ -36,7 +38,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(options, newTree.DiagnosticOptions) End Sub - + Public Sub ParseTreeNullDiagnosticOptions() Dim tree = VisualBasicSyntaxTree.ParseText( SourceText.From(""), @@ -47,7 +49,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.NotSame(ImmutableDictionary(Of String, ReportDiagnostic).Empty, tree.DiagnosticOptions) End Sub - + Public Sub ParseTreeEmptyDiagnosticOptions() Dim tree = VisualBasicSyntaxTree.ParseText( SourceText.From(""), @@ -57,7 +59,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(ImmutableDictionary(Of String, ReportDiagnostic).Empty, tree.DiagnosticOptions) End Sub - + Public Sub ParseTreeCustomDiagnosticOptions() Dim options = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) Dim tree = VisualBasicSyntaxTree.ParseText( @@ -66,14 +68,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(options, tree.DiagnosticOptions) End Sub - + Public Sub DefaultTreeDiagnosticOptions() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Assert.NotNull(tree.DiagnosticOptions) Assert.True(tree.DiagnosticOptions.IsEmpty) End Sub - + Public Sub WithDiagnosticOptionsNull() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Dim newTree = tree.WithDiagnosticOptions(Nothing) @@ -82,7 +84,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(tree, newTree) End Sub - + Public Sub WithDiagnosticOptionsEmpty() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Dim newTree = tree.WithDiagnosticOptions(ImmutableDictionary(Of String, ReportDiagnostic).Empty) @@ -92,7 +94,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.NotSame(tree.DiagnosticOptions, newTree.DiagnosticOptions) End Sub - + Public Sub PerTreeDiagnosticOptionsNewDict() Dim tree = SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()) Dim map = CreateImmutableDictionary(("BC000", ReportDiagnostic.Suppress)) @@ -101,6 +103,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(map, newTree.DiagnosticOptions) Assert.NotEqual(tree, newTree) End Sub +#Enable Warning BC40000 Public Sub WithRootAndOptions_ParsedTree() From 3664c31259eb7c06692cd87e270746402d17fc67 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 30 Sep 2022 10:41:43 -0700 Subject: [PATCH 28/32] RPS fix --- .../EditAndContinueHostWorkspaceEventListener.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs index cb60e1ba2a151..e67140dbd2298 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs @@ -35,11 +35,6 @@ private static void WorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) { Debug.Assert(sender is Workspace); - if (!DebuggerContractVersionCheck.IsRequiredDebuggerContractVersionAvailable()) - { - return; - } - if (e.DocumentId == null) { return; @@ -72,8 +67,19 @@ public void StopListening(Workspace workspace) workspace.WorkspaceChanged -= WorkspaceChanged; } + // NoInlining to avoid loading Debugger.Contracts in certain RPS scenarios. [MethodImpl(MethodImplOptions.NoInlining)] private static void WorkspaceDocumentLoaderChanged(Workspace workspace, Document document) + { + if (DebuggerContractVersionCheck.IsRequiredDebuggerContractVersionAvailable()) + { + WorkspaceDocumentLoaderChangedImpl(workspace, document); + } + } + + // NoInlining to avoid loading proxy type that might require newer version of Debugger.Contracts than available (only applicable to Integration Tests). + [MethodImpl(MethodImplOptions.NoInlining)] + private static void WorkspaceDocumentLoaderChangedImpl(Workspace workspace, Document document) { var proxy = new RemoteEditAndContinueServiceProxy(workspace); _ = Task.Run(() => proxy.OnSourceFileUpdatedAsync(document, CancellationToken.None)).ReportNonFatalErrorAsync(); From 06c2f1bcf7c2205aafcc6f7c7e2d6522b4986d4c Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 6 Oct 2022 09:52:44 -0700 Subject: [PATCH 29/32] PdbMatchingSourceTextProvider --- .../InternalUtilities/EnumerableExtensions.cs | 15 + ...itAndContinueHostWorkspaceEventListener.cs | 88 ---- .../EditAndContinueLanguageService.cs | 51 +- .../PdbMatchingSourceTextProvider.cs | 173 +++++++ .../EditAndContinueWorkspaceServiceTests.cs | 450 +++++++++++------- .../EditSessionActiveStatementsTests.cs | 1 + .../Helpers/MockHostWorkspaceProvider.cs | 26 + .../Helpers/MockManagedHotReloadService.cs | 36 ++ .../MockPdbMatchingSourceTextProvider.cs | 20 + .../RemoteEditAndContinueServiceTests.cs | 18 +- .../MockEditAndContinueWorkspaceService.cs | 6 +- .../EditAndContinue/CommittedSolution.cs | 24 +- .../EditAndContinue/DebuggingSession.cs | 15 +- .../EditAndContinueWorkspaceService.cs | 15 +- .../IEditAndContinueWorkspaceService.cs | 4 +- .../IPdbMatchingSourceTextProvider.cs | 30 ++ .../Remote/IRemoteEditAndContinueService.cs | 2 +- .../RemoteEditAndContinueServiceProxy.cs | 57 ++- .../API/UnitTestingHotReloadService.cs | 2 + .../Watch/Api/WatchHotReloadService.cs | 1 + .../Debugger/GlassTestsHotReloadService.cs | 1 + .../RemoteEditAndContinueService.cs | 38 +- 22 files changed, 696 insertions(+), 377 deletions(-) delete mode 100644 src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs create mode 100644 src/EditorFeatures/Core/EditAndContinue/PdbMatchingSourceTextProvider.cs create mode 100644 src/EditorFeatures/Test/EditAndContinue/Helpers/MockHostWorkspaceProvider.cs create mode 100644 src/EditorFeatures/Test/EditAndContinue/Helpers/MockManagedHotReloadService.cs create mode 100644 src/EditorFeatures/Test/EditAndContinue/Helpers/MockPdbMatchingSourceTextProvider.cs create mode 100644 src/Features/Core/Portable/EditAndContinue/IPdbMatchingSourceTextProvider.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/EnumerableExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/EnumerableExtensions.cs index c017a55043b25..6fbe111a0970e 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/EnumerableExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/EnumerableExtensions.cs @@ -338,6 +338,21 @@ public static async ValueTask> SelectAsArrayAsync + /// Maps an immutable array through a function that returns ValueTask, returning the new ImmutableArray. + /// + public static async ValueTask> SelectAsArrayAsync(this IEnumerable source, Func> selector, CancellationToken cancellationToken) + { + var builder = ArrayBuilder.GetInstance(); + + foreach (var item in source) + { + builder.Add(await selector(item, cancellationToken).ConfigureAwait(false)); + } + + return builder.ToImmutableAndFree(); + } + /// /// Maps an immutable array through a function that returns ValueTask, returning the new ImmutableArray. /// diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs deleted file mode 100644 index e67140dbd2298..0000000000000 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Composition; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.EditAndContinue -{ - /// - /// Notifies EnC service of host workspace events. - /// - [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] - internal sealed class EditAndContinueHostWorkspaceEventListener : IEventListener, IEventListenerStoppable - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public EditAndContinueHostWorkspaceEventListener() - { - } - - public void StartListening(Workspace workspace, object serviceOpt) - { - workspace.WorkspaceChanged += WorkspaceChanged; - } - - private static void WorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) - { - Debug.Assert(sender is Workspace); - - if (e.DocumentId == null) - { - return; - } - - var oldDocument = e.OldSolution.GetDocument(e.DocumentId); - if (oldDocument == null) - { - // document added - return; - } - - var newDocument = e.NewSolution.GetDocument(e.DocumentId); - if (newDocument == null) - { - // document removed - return; - } - - // When a document is open its loader transitions from file-based loader to text buffer based. - // The file checksum is no longer available from the latter, so capture it at this moment. - if (oldDocument.State.TextAndVersionSource.CanReloadText && !newDocument.State.TextAndVersionSource.CanReloadText) - { - WorkspaceDocumentLoaderChanged((Workspace)sender, oldDocument); - } - } - - public void StopListening(Workspace workspace) - { - workspace.WorkspaceChanged -= WorkspaceChanged; - } - - // NoInlining to avoid loading Debugger.Contracts in certain RPS scenarios. - [MethodImpl(MethodImplOptions.NoInlining)] - private static void WorkspaceDocumentLoaderChanged(Workspace workspace, Document document) - { - if (DebuggerContractVersionCheck.IsRequiredDebuggerContractVersionAvailable()) - { - WorkspaceDocumentLoaderChangedImpl(workspace, document); - } - } - - // NoInlining to avoid loading proxy type that might require newer version of Debugger.Contracts than available (only applicable to Integration Tests). - [MethodImpl(MethodImplOptions.NoInlining)] - private static void WorkspaceDocumentLoaderChangedImpl(Workspace workspace, Document document) - { - var proxy = new RemoteEditAndContinueServiceProxy(workspace); - _ = Task.Run(() => proxy.OnSourceFileUpdatedAsync(document, CancellationToken.None)).ReportNonFatalErrorAsync(); - } - } -} diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index 70d90cecc94f9..f71e0201f0a7c 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -5,14 +5,12 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Roslyn.Utilities; @@ -26,6 +24,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue [ExportMetadata("UIContext", EditAndContinueUIContext.EncCapableProjectExistsInWorkspaceUIContextString)] internal sealed class EditAndContinueLanguageService : IManagedHotReloadLanguageService, IEditAndContinueSolutionProvider { + private readonly PdbMatchingSourceTextProvider _sourceTextProvider; private readonly Lazy _debuggerService; private readonly IDiagnosticAnalyzerService _diagnosticService; private readonly EditAndContinueDiagnosticUpdateSource _diagnosticUpdateSource; @@ -52,12 +51,14 @@ public EditAndContinueLanguageService( Lazy workspaceProvider, Lazy debuggerService, IDiagnosticAnalyzerService diagnosticService, - EditAndContinueDiagnosticUpdateSource diagnosticUpdateSource) + EditAndContinueDiagnosticUpdateSource diagnosticUpdateSource, + PdbMatchingSourceTextProvider sourceTextProvider) { WorkspaceProvider = workspaceProvider; _debuggerService = debuggerService; _diagnosticService = diagnosticService; _diagnosticUpdateSource = diagnosticUpdateSource; + _sourceTextProvider = sourceTextProvider; } private Solution GetCurrentCompileTimeSolution(Solution? currentDesignTimeSolution = null) @@ -93,15 +94,24 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken) try { + // Activate listener before capturing the current solution snapshot, + // so that we don't miss any pertinent workspace update events. + _sourceTextProvider.Activate(); + var workspace = WorkspaceProvider.Value.Workspace; - var solution = GetCurrentCompileTimeSolution(_committedDesignTimeSolution = workspace.CurrentSolution); - var openedDocumentIds = workspace.GetOpenDocumentIds().ToImmutableArray(); + var currentSolution = workspace.CurrentSolution; + _committedDesignTimeSolution = currentSolution; + var solution = GetCurrentCompileTimeSolution(currentSolution); + + _sourceTextProvider.SetBaseline(currentSolution); + var proxy = new RemoteEditAndContinueServiceProxy(workspace); _debuggingSession = await proxy.StartDebuggingSessionAsync( solution, new ManagedHotReloadServiceImpl(_debuggerService.Value), - captureMatchingDocuments: openedDocumentIds, + _sourceTextProvider, + captureMatchingDocuments: ImmutableArray.Empty, captureAllMatchingDocuments: false, reportDiagnostics: true, cancellationToken).ConfigureAwait(false); @@ -228,24 +238,23 @@ public async ValueTask EndSessionAsync(CancellationToken cancellationToken) { IsSessionActive = false; - if (_disabled) + if (!_disabled) { - return; + try + { + var solution = GetCurrentCompileTimeSolution(); + await GetDebuggingSession().EndDebuggingSessionAsync(solution, _diagnosticUpdateSource, _diagnosticService, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + _disabled = true; + } } - try - { - var solution = GetCurrentCompileTimeSolution(); - await GetDebuggingSession().EndDebuggingSessionAsync(solution, _diagnosticUpdateSource, _diagnosticService, cancellationToken).ConfigureAwait(false); - - _debuggingSession = null; - _committedDesignTimeSolution = null; - _pendingUpdatedDesignTimeSolution = null; - } - catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) - { - _disabled = true; - } + _sourceTextProvider.Deactivate(); + _debuggingSession = null; + _committedDesignTimeSolution = null; + _pendingUpdatedDesignTimeSolution = null; } private ActiveStatementSpanProvider GetActiveStatementSpanProvider(Solution solution) diff --git a/src/EditorFeatures/Core/EditAndContinue/PdbMatchingSourceTextProvider.cs b/src/EditorFeatures/Core/EditAndContinue/PdbMatchingSourceTextProvider.cs new file mode 100644 index 0000000000000..af8ef44b7bee4 --- /dev/null +++ b/src/EditorFeatures/Core/EditAndContinue/PdbMatchingSourceTextProvider.cs @@ -0,0 +1,173 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.EditAndContinue +{ + /// + /// Notifies EnC service of host workspace events. + /// + [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] + [Export(typeof(PdbMatchingSourceTextProvider))] + internal sealed class PdbMatchingSourceTextProvider : IEventListener, IEventListenerStoppable, IPdbMatchingSourceTextProvider + { + private readonly object _guard = new(); + + private bool _isActive; + private int _baselineSolutionVersion; + private readonly Dictionary _documentsWithChangedLoaderByPath = new(); + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public PdbMatchingSourceTextProvider() + { + } + + public void StartListening(Workspace workspace, object serviceOpt) + { + workspace.WorkspaceChanged += WorkspaceChanged; + } + + public void StopListening(Workspace workspace) + { + workspace.WorkspaceChanged -= WorkspaceChanged; + } + + private void WorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) + { + if (!_isActive) + { + // Not capturing document states because debugging session isn't active. + return; + } + + if (e.DocumentId == null) + { + return; + } + + var oldDocument = e.OldSolution.GetDocument(e.DocumentId); + if (oldDocument == null) + { + // document added + return; + } + + var newDocument = e.NewSolution.GetDocument(e.DocumentId); + if (newDocument == null) + { + // document removed + return; + } + + if (!oldDocument.State.SupportsEditAndContinue()) + { + return; + } + + Contract.ThrowIfNull(oldDocument.FilePath); + + // When a document is open its loader transitions from file-based loader to text buffer based. + // The file checksum is no longer available from the latter, so capture it at this moment. + if (oldDocument.State.TextAndVersionSource.CanReloadText && !newDocument.State.TextAndVersionSource.CanReloadText) + { + var oldSolutionVersion = oldDocument.Project.Solution.WorkspaceVersion; + + lock (_guard) + { + // ignore updates to a document that we have already seen this session: + if (_isActive && oldSolutionVersion >= _baselineSolutionVersion && !_documentsWithChangedLoaderByPath.ContainsKey(oldDocument.FilePath)) + { + _documentsWithChangedLoaderByPath.Add(oldDocument.FilePath, (oldDocument.DocumentState, oldSolutionVersion)); + } + } + } + } + + /// + /// Establish a baseline snapshot. The listener will ignore all document snapshots that are older. + /// + public void SetBaseline(Solution solution) + { + lock (_guard) + { + _baselineSolutionVersion = solution.WorkspaceVersion; + } + } + + public void Activate() + { + lock (_guard) + { + _isActive = true; + } + } + + public void Deactivate() + { + lock (_guard) + { + _isActive = false; + _documentsWithChangedLoaderByPath.Clear(); + } + } + + public async ValueTask TryGetMatchingSourceTextAsync(string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + { + DocumentState? state; + lock (_guard) + { + if (!_documentsWithChangedLoaderByPath.TryGetValue(filePath, out var stateAndVersion)) + { + return null; + } + + state = stateAndVersion.state; + } + + if (state.LoadTextOptions.ChecksumAlgorithm != checksumAlgorithm) + { + return null; + } + + var text = await state.GetTextAsync(cancellationToken).ConfigureAwait(false); + if (!text.GetChecksum().SequenceEqual(requiredChecksum)) + { + return null; + } + + return text.ToString(); + } + + internal TestAccessor GetTestAccessor() + => new(this); + + internal readonly struct TestAccessor + { + private readonly PdbMatchingSourceTextProvider _instance; + + internal TestAccessor(PdbMatchingSourceTextProvider instance) + => _instance = instance; + + public ImmutableDictionary GetDocumentsWithChangedLoaderByPath() + { + lock (_instance._guard) + { + return _instance._documentsWithChangedLoaderByPath.ToImmutableDictionary(); + } + } + } + } +} diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index e1b0b1a7ce5d0..285f26ab738ae 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -21,14 +21,21 @@ using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.EditAndContinue.Contracts; +using Microsoft.CodeAnalysis.Editor.UnitTests; +using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; using Microsoft.CodeAnalysis.ExternalAccess.Watch.Api; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; +using Microsoft.VisualStudio.Text; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; @@ -41,7 +48,6 @@ namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests [UseExportProvider] public sealed partial class EditAndContinueWorkspaceServiceTests : TestBase { - private static readonly TestComposition s_composition = FeaturesTestCompositions.Features; private static readonly Guid s_solutionTelemetryId = Guid.Parse("00000000-AAAA-AAAA-AAAA-000000000000"); private static readonly Guid s_defaultProjectTelemetryId = Guid.Parse("00000000-AAAA-AAAA-AAAA-111111111111"); @@ -68,14 +74,32 @@ public EditAndContinueWorkspaceServiceTests() private TestWorkspace CreateWorkspace(out Solution solution, out EditAndContinueWorkspaceService service, Type[] additionalParts = null) { - var workspace = new TestWorkspace(composition: s_composition.AddParts(additionalParts), solutionTelemetryId: s_solutionTelemetryId); + var workspace = new TestWorkspace(composition: FeaturesTestCompositions.Features.AddParts(additionalParts), solutionTelemetryId: s_solutionTelemetryId); solution = workspace.CurrentSolution; service = GetEditAndContinueService(workspace); return workspace; } + private TestWorkspace CreateEditorWorkspace(out Solution solution, out EditAndContinueWorkspaceService service, out EditAndContinueLanguageService languageService, Type[] additionalParts = null) + { + var composition = EditorTestCompositions.EditorFeatures + .RemoveParts(typeof(MockWorkspaceEventListenerProvider)) + .AddParts( + typeof(MockHostWorkspaceProvider), + typeof(MockManagedHotReloadService)) + .AddParts(additionalParts); + + var workspace = new TestWorkspace(composition: composition, solutionTelemetryId: s_solutionTelemetryId); + ((MockHostWorkspaceProvider)workspace.GetService()).Workspace = workspace; + + solution = workspace.CurrentSolution; + service = GetEditAndContinueService(workspace); + languageService = workspace.GetService(); + return workspace; + } + private static SourceText GetAnalyzerConfigText((string key, string value)[] analyzerConfig) - => SourceText.From("[*.*]" + Environment.NewLine + string.Join(Environment.NewLine, analyzerConfig.Select(c => $"{c.key} = {c.value}"))); + => CreateText("[*.*]" + Environment.NewLine + string.Join(Environment.NewLine, analyzerConfig.Select(c => $"{c.key} = {c.value}"))); private static (Solution, Document) AddDefaultTestProject( Solution solution, @@ -110,7 +134,7 @@ private static Solution AddDefaultTestProject( if (additionalFileText != null) { - solution = solution.AddAdditionalDocument(DocumentId.CreateNewId(project.Id), "additional", SourceText.From(additionalFileText)); + solution = solution.AddAdditionalDocument(DocumentId.CreateNewId(project.Id), "additional", CreateText(additionalFileText)); } if (analyzerConfig != null) @@ -129,7 +153,7 @@ private static Solution AddDefaultTestProject( var fileName = $"test{i++}.cs"; document = solution.GetProject(project.Id). - AddDocument(fileName, SourceText.From(source, Encoding.UTF8), filePath: Path.Combine(TempRoot.Root, fileName)); + AddDocument(fileName, CreateText(source), filePath: Path.Combine(TempRoot.Root, fileName)); solution = document.Project.Solution; } @@ -148,11 +172,13 @@ private EditAndContinueWorkspaceService GetEditAndContinueService(Workspace work private async Task StartDebuggingSessionAsync( EditAndContinueWorkspaceService service, Solution solution, - CommittedSolution.DocumentState initialState = CommittedSolution.DocumentState.MatchesBuildOutput) + CommittedSolution.DocumentState initialState = CommittedSolution.DocumentState.MatchesBuildOutput, + IPdbMatchingSourceTextProvider sourceTextProvider = null) { var sessionId = await service.StartDebuggingSessionAsync( solution, _debuggerService, + sourceTextProvider: sourceTextProvider ?? NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments: ImmutableArray.Empty, captureAllMatchingDocuments: false, reportDiagnostics: true, @@ -243,9 +269,9 @@ internal static Guid ReadModuleVersionId(Stream stream) return metadataReader.GetGuid(mvidHandle); } - private Guid EmitAndLoadLibraryToDebuggee(string source, string sourceFilePath = null, Encoding encoding = null, string assemblyName = "") + private Guid EmitAndLoadLibraryToDebuggee(string source, string sourceFilePath = null, Encoding encoding = null, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default, string assemblyName = "") { - var moduleId = EmitLibrary(source, sourceFilePath, encoding, assemblyName); + var moduleId = EmitLibrary(source, sourceFilePath, encoding, checksumAlgorithm, assemblyName); LoadLibraryToDebuggee(moduleId); return moduleId; } @@ -259,18 +285,20 @@ private Guid EmitLibrary( string source, string sourceFilePath = null, Encoding encoding = null, + SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default, string assemblyName = "", DebugInformationFormat pdbFormat = DebugInformationFormat.PortablePdb, ISourceGenerator generator = null, string additionalFileText = null, IEnumerable<(string, string)> analyzerOptions = null) { - return EmitLibrary(new[] { (source, sourceFilePath ?? Path.Combine(TempRoot.Root, "test1.cs")) }, encoding, assemblyName, pdbFormat, generator, additionalFileText, analyzerOptions); + return EmitLibrary(new[] { (source, sourceFilePath ?? Path.Combine(TempRoot.Root, "test1.cs")) }, encoding, checksumAlgorithm, assemblyName, pdbFormat, generator, additionalFileText, analyzerOptions); } private Guid EmitLibrary( (string content, string filePath)[] sources, Encoding encoding = null, + SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default, string assemblyName = "", DebugInformationFormat pdbFormat = DebugInformationFormat.PortablePdb, ISourceGenerator generator = null, @@ -283,7 +311,7 @@ private Guid EmitLibrary( var trees = sources.Select(source => { - var sourceText = SourceText.From(new MemoryStream(encoding.GetBytes(source.content)), encoding, checksumAlgorithm: SourceHashAlgorithms.Default); + var sourceText = SourceText.From(new MemoryStream(encoding.GetBytesWithPreamble(source.content)), encoding, checksumAlgorithm); return SyntaxFactory.ParseSyntaxTree(sourceText, parseOptions, source.filePath); }); @@ -332,7 +360,10 @@ private Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbForm return moduleId; } - private static SourceText CreateSourceTextFromFile(string path) + private static SourceText CreateText(string source) + => SourceText.From(source, Encoding.UTF8, SourceHashAlgorithms.Default); + + private static SourceText CreateTextFromFile(string path) { using var stream = File.OpenRead(path); return SourceText.From(stream, Encoding.UTF8, SourceHashAlgorithms.Default); @@ -361,7 +392,7 @@ private static void VerifyReadersDisposed(IEnumerable readers) private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, string name = "design-time-only.cs", string path = "design-time-only.cs") { - var sourceText = SourceText.From("class DTO {}", Encoding.UTF8, SourceHashAlgorithms.Default); + var sourceText = CreateText("class DTO {}"); return DocumentInfo.Create( DocumentId.CreateNewId(projectId, name), name: name, @@ -467,16 +498,17 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume var sourceC1 = "class C { const char L = 'ワ'; }"; var sourceD1 = "dummy code"; var sourceE1 = "class E { }"; - var sourceBytesA1 = encodingA.GetBytes(sourceA1); - var sourceBytesB1 = encodingB.GetBytes(sourceB1); - var sourceBytesC1 = encodingC.GetBytes(sourceC1); - var sourceBytesE1 = encodingE.GetBytes(sourceE1); + var sourceBytesA1 = encodingA.GetBytesWithPreamble(sourceA1); + var sourceBytesB1 = encodingB.GetBytesWithPreamble(sourceB1); + var sourceBytesC1 = encodingC.GetBytesWithPreamble(sourceC1); + var sourceBytesD1 = Encoding.UTF8.GetBytesWithPreamble(sourceD1); + var sourceBytesE1 = encodingE.GetBytesWithPreamble(sourceE1); var dir = Temp.CreateDirectory(); var sourceFileA = dir.CreateFile("A.cs").WriteAllBytes(sourceBytesA1); var sourceFileB = dir.CreateFile("B.cs").WriteAllBytes(sourceBytesB1); var sourceFileC = dir.CreateFile("C.cs").WriteAllBytes(sourceBytesC1); - var sourceFileD = dir.CreateFile("dummy").WriteAllText(sourceD1); + var sourceFileD = dir.CreateFile("dummy").WriteAllBytes(sourceBytesD1); var sourceFileE = dir.CreateFile("E.cs").WriteAllBytes(sourceBytesE1); var sourceTreeA1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesA1, sourceBytesA1.Length, encodingA, SourceHashAlgorithms.Default), TestOptions.Regular, sourceFileA.Path); var sourceTreeB1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesB1, sourceBytesB1.Length, encodingB, SourceHashAlgorithms.Default), TestOptions.Regular, sourceFileB.Path); @@ -550,7 +582,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume ImmutableArray.Empty : (from project in solution.Projects from documentId in project.DocumentIds select documentId).ToImmutableArray(); - var sessionId = await service.StartDebuggingSessionAsync(solution, _debuggerService, captureMatchingDocuments, captureAllDocuments, reportDiagnostics: true, CancellationToken.None); + var sessionId = await service.StartDebuggingSessionAsync(solution, _debuggerService, NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments, captureAllDocuments, reportDiagnostics: true, CancellationToken.None); var debuggingSession = service.GetTestAccessor().GetDebuggingSession(sessionId); var matchingDocuments = debuggingSession.LastCommittedSolution.Test_GetDocumentStates(); @@ -589,7 +621,7 @@ public async Task ProjectNotBuilt() Assert.Empty(diagnostics); // change the source: - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }")); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M() { System.Console.WriteLine(2); } }")); var document2 = solution.GetDocument(document1.Id); diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noActiveSpans, CancellationToken.None); @@ -620,7 +652,7 @@ public async Task DifferentDocumentWithSameContent() // update the document var document1 = solution.GetDocument(document.Id); - solution = solution.WithDocumentText(document.Id, SourceText.From(source)); + solution = solution.WithDocumentText(document.Id, CreateText(source)); var document2 = solution.GetDocument(document.Id); Assert.Equal(document1.Id, document2.Id); @@ -647,7 +679,7 @@ public async Task ProjectThatDoesNotSupportEnC(bool breakMode) { using var _ = CreateWorkspace(out var solution, out var service, new[] { typeof(NoCompilationLanguageService) }); var project = solution.AddProject("dummy_proj", "dummy_proj", NoCompilationConstants.LanguageName); - var document = project.AddDocument("test", SourceText.From("dummy1")); + var document = project.AddDocument("test", CreateText("dummy1")); solution = document.Project.Solution; var debuggingSession = await StartDebuggingSessionAsync(service, solution); @@ -662,7 +694,7 @@ public async Task ProjectThatDoesNotSupportEnC(bool breakMode) Assert.Empty(diagnostics); // change the source: - solution = solution.WithDocumentText(document1.Id, SourceText.From("dummy2")); + solution = solution.WithDocumentText(document1.Id, CreateText("dummy2")); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -691,7 +723,7 @@ public async Task DesignTimeOnlyDocument() var debuggingSession = await StartDebuggingSessionAsync(service, solution); // update a design-time-only source file: - solution = solution.WithDocumentText(documentInfo.Id, SourceText.From("class UpdatedC2 {}")); + solution = solution.WithDocumentText(documentInfo.Id, CreateText("class UpdatedC2 {}")); var document2 = solution.GetDocument(documentInfo.Id); // no updates: @@ -717,7 +749,7 @@ public async Task DesignTimeOnlyDocument_Dynamic() (solution, var document) = AddDefaultTestProject(solution, "class C {}"); - var sourceText = SourceText.From("class D {}", Encoding.UTF8, SourceHashAlgorithms.Default); + var sourceText = CreateText("class D {}"); var documentInfo = DocumentInfo.Create( DocumentId.CreateNewId(document.Project.Id), name: "design-time-only.cs", @@ -733,7 +765,7 @@ public async Task DesignTimeOnlyDocument_Dynamic() // change the source: var document1 = solution.GetDocument(documentInfo.Id); - solution = solution.WithDocumentText(document1.Id, SourceText.From("class E {}")); + solution = solution.WithDocumentText(document1.Id, CreateText("class E {}")); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -756,7 +788,7 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. var sourceB2 = (language == LanguageNames.CSharp) ? "class B2 { }" : "Class C2 : End Class"; var dir = Temp.CreateDirectory(); - var sourceFileA = dir.CreateFile("a.cs").WriteAllText(sourceA); + var sourceFileA = dir.CreateFile("a.cs").WriteAllText(sourceA, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); @@ -768,8 +800,8 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. solution = solution. AddProject(projectId, "test", "test", language). - AddDocument(documentAId, "a.xx", SourceText.From(sourceA, Encoding.UTF8), filePath: sourceFileA.Path). - AddDocument(documentBId, "b.g.i.xx", SourceText.From(sourceB, Encoding.UTF8), filePath: Path.Combine(dir.Path, "b.g.i.xx")). + AddDocument(documentAId, "a.xx", CreateText(sourceA), filePath: sourceFileA.Path). + AddDocument(documentBId, "b.g.i.xx", CreateText(sourceB), filePath: Path.Combine(dir.Path, "b.g.i.xx")). AddMetadataReferences(projectId, TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)); // only compile A; B is design-time-only: @@ -784,7 +816,7 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. _debuggerService.GetCapabilitiesImpl = () => ImmutableArray.Create("Baseline"); var openDocumentIds = open ? ImmutableArray.Create(documentBId) : ImmutableArray.Empty; - var sessionId = await service.StartDebuggingSessionAsync(solution, _debuggerService, captureMatchingDocuments: openDocumentIds, captureAllMatchingDocuments: false, reportDiagnostics: true, CancellationToken.None); + var sessionId = await service.StartDebuggingSessionAsync(solution, _debuggerService, NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments: openDocumentIds, captureAllMatchingDocuments: false, reportDiagnostics: true, CancellationToken.None); var debuggingSession = service.GetTestAccessor().GetDebuggingSession(sessionId); var documentB = solution.GetDocument(documentBId); @@ -800,7 +832,7 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. EnterBreakState(debuggingSession, activeStatements); // change the source (rude edit): - solution = solution.WithDocumentText(documentBId, SourceText.From(sourceB2)); + solution = solution.WithDocumentText(documentBId, CreateText(sourceB2)); var documentB2 = solution.GetDocument(documentBId); @@ -870,7 +902,7 @@ public async Task ErrorReadingModuleFile(bool breakMode) } // change the source: - solution = solution.WithDocumentText(document1.Id, SourceText.From(source2, encoding: Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(source2)); var document2 = solution.GetDocument(document1.Id); // error not reported here since it might be intermittent and will be reported if the issue persist when applying the update: @@ -924,14 +956,14 @@ public async Task ErrorReadingPdbFile() var source1 = "class C1 { void M() { System.Console.WriteLine(1); } }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1); + var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); var document1 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument("a.cs", SourceText.From(source1, Encoding.UTF8), filePath: sourceFile.Path); + AddDocument("a.cs", CreateText(source1), filePath: sourceFile.Path); var project = document1.Project; solution = project.Solution; @@ -950,7 +982,7 @@ public async Task ErrorReadingPdbFile() EnterBreakState(debuggingSession); // change the source: - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M() { System.Console.WriteLine(2); } }")); var document2 = solution.GetDocument(document1.Id); // error not reported here since it might be intermittent and will be reported if the issue persist when applying the update: @@ -977,25 +1009,25 @@ public async Task ErrorReadingSourceFile() var source1 = "class C1 { void M() { System.Console.WriteLine(1); } }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1); + var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); var document1 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(DefaultTargetFramework)). - AddDocument("a.cs", SourceText.From(source1, Encoding.UTF8, SourceHashAlgorithms.Default), filePath: sourceFile.Path); + AddDocument("a.cs", SourceText.From(source1, Encoding.UTF8, SourceHashAlgorithm.Sha1), filePath: sourceFile.Path); var project = document1.Project; solution = project.Solution; - var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); + var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path, checksumAlgorithm: SourceHashAlgorithms.Default); var debuggingSession = await StartDebuggingSessionAsync(service, solution, initialState: CommittedSolution.DocumentState.None); EnterBreakState(debuggingSession); // change the source: - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8, SourceHashAlgorithms.Default)); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M() { System.Console.WriteLine(2); } }")); var document2 = solution.GetDocument(document1.Id); using var fileLock = File.Open(sourceFile.Path, FileMode.Open, FileAccess.Read, FileShare.None); @@ -1035,15 +1067,15 @@ public async Task FileAdded(bool breakMode) var sourceA = "class C1 { void M() { System.Console.WriteLine(1); } }"; var sourceB = "class C2 {}"; - var sourceFileA = Temp.CreateFile().WriteAllText(sourceA); - var sourceFileB = Temp.CreateFile().WriteAllText(sourceB); + var sourceFileA = Temp.CreateFile().WriteAllText(sourceA, Encoding.UTF8); + var sourceFileB = Temp.CreateFile().WriteAllText(sourceB, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); var documentA = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument("test.cs", SourceText.From(sourceA, Encoding.UTF8), filePath: sourceFileA.Path); + AddDocument("test.cs", CreateText(sourceA), filePath: sourceFileA.Path); solution = documentA.Project.Solution; @@ -1060,7 +1092,7 @@ public async Task FileAdded(bool breakMode) } // add a source file: - var documentB = project.AddDocument("file2.cs", SourceText.From(sourceB, Encoding.UTF8), filePath: sourceFileB.Path); + var documentB = project.AddDocument("file2.cs", CreateText(sourceB), filePath: sourceFileB.Path); solution = documentB.Project.Solution; documentB = solution.GetDocument(documentB.Id); @@ -1137,10 +1169,10 @@ public async Task ModuleDisallowsEditAndContinue_NoChanges(bool breakMode) LoadLibraryToDebuggee(moduleId, new ManagedHotReloadAvailability(ManagedHotReloadAvailabilityStatus.NotAllowedForRuntime, "*message*")); // update the file with source1 before session starts: - sourceFile.WriteAllText(source1); + sourceFile.WriteAllText(source1, Encoding.UTF8); // source0 is loaded to workspace before session starts: - var document0 = project.AddDocument("a.cs", SourceText.From(source0, Encoding.UTF8), filePath: sourceFile.Path); + var document0 = project.AddDocument("a.cs", CreateText(source0), filePath: sourceFile.Path); solution = document0.Project.Solution; var debuggingSession = await StartDebuggingSessionAsync(service, solution, initialState: CommittedSolution.DocumentState.None); @@ -1151,7 +1183,7 @@ public async Task ModuleDisallowsEditAndContinue_NoChanges(bool breakMode) } // workspace is updated to new version after build completed and the session started: - solution = solution.WithDocumentText(document0.Id, SourceText.From(source1)); + solution = solution.WithDocumentText(document0.Id, CreateText(source1)); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Equal(ModuleUpdateStatus.None, updates.Status); @@ -1189,7 +1221,7 @@ public async Task ModuleDisallowsEditAndContinue_SourceGenerator_NoChanges() // update document with the same content: var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From(source2)); + solution = solution.WithDocumentText(document1.Id, CreateText(source2)); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Equal(ModuleUpdateStatus.None, updates.Status); @@ -1239,7 +1271,7 @@ void M() // change the source: var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From(source2)); + solution = solution.WithDocumentText(document1.Id, CreateText(source2)); var document2 = solution.GetDocument(document1.Id); // We do not report module diagnostics until emit. @@ -1265,6 +1297,17 @@ void M() }, _telemetryLog); } + private class TestSourceTextContainer : SourceTextContainer + { + public SourceText Text { get; set; } + + public override SourceText CurrentText => Text; + +#pragma warning disable CS0067 + public override event EventHandler TextChanged; +#pragma warning restore + } + [Fact] public async Task Encodings() { @@ -1275,33 +1318,42 @@ public async Task Encodings() var dir = Temp.CreateDirectory(); var sourceFile = dir.CreateFile("test.cs").WriteAllText(source1, encoding); - using var _ = CreateWorkspace(out var solution, out var service); + using var workspace = CreateWorkspace(out var solution, out var service); - var document1 = solution. - AddProject("test", "test", LanguageNames.CSharp). - AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument("test.cs", SourceText.From(source1, encoding), filePath: sourceFile.Path); + var projectId = ProjectId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); - var documentId = document1.Id; + solution = solution. + AddProject(projectId, "test", "test", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithm.Sha1). + AddMetadataReferences(projectId, TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). + AddDocument(documentId, "test.cs", SourceText.From(source1, encoding, SourceHashAlgorithm.Sha1), filePath: sourceFile.Path); - var project = document1.Project; - solution = project.Solution; + // use different checksum alg to trigger PdbMatchingSourceTextProvider call: + var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path, encoding: encoding, checksumAlgorithm: SourceHashAlgorithm.Sha256); - var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path, encoding: encoding); + var sourceTextProviderCalled = false; + var sourceTextProvider = new MockPdbMatchingSourceTextProvider() + { + TryGetMatchingSourceTextImpl = (filePath, requiredChecksum, checksumAlgorithm) => + { + sourceTextProviderCalled = true; - var debuggingSession = await StartDebuggingSessionAsync(service, solution, initialState: CommittedSolution.DocumentState.None); + // fall back to reading the file content: + return null; + } + }; + + var debuggingSession = await StartDebuggingSessionAsync(service, solution, initialState: CommittedSolution.DocumentState.None, sourceTextProvider); EnterBreakState(debuggingSession); - // Emulate opening the file, which will trigger "out-of-sync" check. - // Since we find content matching the PDB checksum we update the committed solution with this source text. - // If we used wrong encoding this would lead to a false change detected below. - var currentDocument = solution.GetDocument(documentId); - await debuggingSession.OnSourceFileUpdatedAsync(currentDocument); + var (document, state) = await debuggingSession.LastCommittedSolution.GetDocumentAndStateAsync(documentId, currentDocument: null, CancellationToken.None); + var text = await document.GetTextAsync(); + Assert.Same(encoding, text.Encoding); + Assert.Equal(CommittedSolution.DocumentState.MatchesBuildOutput, state); - // EnC service queries for a document, which triggers read of the source file from disk. - var (updates, _) = await EmitSolutionUpdateAsync(debuggingSession, solution); - Assert.Equal(ModuleUpdateStatus.None, updates.Status); + Assert.True(sourceTextProviderCalled); EndDebuggingSession(debuggingSession); } @@ -1329,7 +1381,7 @@ public async Task RudeEdits(bool breakMode) // change the source (rude edit): var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(source2)); var document2 = solution.GetDocument(document1.Id); var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noActiveSpans, CancellationToken.None); @@ -1387,8 +1439,8 @@ public async Task DeferredApplyChangeWithActiveStatementRudeEdits() var debuggingSession = await StartDebuggingSessionAsync(service, solution); - var activeLineSpan1 = SourceText.From(source1, Encoding.UTF8).Lines.GetLinePositionSpan(GetSpan(source1, "System.Console.WriteLine(1);")); - var activeLineSpan2 = SourceText.From(source2, Encoding.UTF8).Lines.GetLinePositionSpan(GetSpan(source2, "System.Console.WriteLine(2);")); + var activeLineSpan1 = CreateText(source1).Lines.GetLinePositionSpan(GetSpan(source1, "System.Console.WriteLine(1);")); + var activeLineSpan2 = CreateText(source2).Lines.GetLinePositionSpan(GetSpan(source2, "System.Console.WriteLine(2);")); var activeStatements = ImmutableArray.Create( new ManagedActiveStatementDebugInfo( @@ -1400,7 +1452,7 @@ public async Task DeferredApplyChangeWithActiveStatementRudeEdits() EnterBreakState(debuggingSession, activeStatements); // change the source (rude edit): - solution = solution.WithDocumentText(document.Id, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(document.Id, CreateText(source2)); var document2 = solution.GetDocument(document.Id); var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noActiveSpans, CancellationToken.None); @@ -1466,7 +1518,7 @@ class C { int Y => 2; } // change the source: var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); var generatedDocument = (await solution.Projects.Single().GetSourceGeneratedDocumentsAsync()).Single(); @@ -1505,10 +1557,10 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) var moduleId = EmitAndLoadLibraryToDebuggee(source0, sourceFilePath: sourceFile.Path); // update the file with source1 before session starts: - sourceFile.WriteAllText(source1); + sourceFile.WriteAllText(source1, Encoding.UTF8); // source1 is reflected in workspace before session starts: - var document1 = project.AddDocument("a.cs", SourceText.From(source1, Encoding.UTF8), filePath: sourceFile.Path); + var document1 = project.AddDocument("a.cs", CreateText(source1), filePath: sourceFile.Path); solution = document1.Project.Solution; var debuggingSession = await StartDebuggingSessionAsync(service, solution, initialState: CommittedSolution.DocumentState.None); @@ -1519,7 +1571,7 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) } // change the source (rude edit): - solution = solution.WithDocumentText(document1.Id, SourceText.From(source2)); + solution = solution.WithDocumentText(document1.Id, CreateText(source2)); var document2 = solution.GetDocument(document1.Id); // no Rude Edits, since the document is out-of-sync @@ -1533,7 +1585,7 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) AssertEx.Equal(new[] { $"{project.Id}: Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}" }, InspectDiagnostics(emitDiagnostics)); // update the file to match the build: - sourceFile.WriteAllText(source0); + sourceFile.WriteAllText(source0, Encoding.UTF8); // we do not reload the content of out-of-sync file for analyzer query: diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noActiveSpans, CancellationToken.None); @@ -1598,7 +1650,7 @@ public async Task RudeEdits_DocumentWithoutSequencePoints() { var source1 = "abstract class C { public abstract void M(); }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1); + var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); @@ -1606,7 +1658,7 @@ public async Task RudeEdits_DocumentWithoutSequencePoints() var document1 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument("test.cs", SourceText.From(source1, Encoding.UTF8), filePath: sourceFile.Path); + AddDocument("test.cs", CreateText(source1), filePath: sourceFile.Path); var project = document1.Project; solution = project.Solution; @@ -1619,7 +1671,7 @@ public async Task RudeEdits_DocumentWithoutSequencePoints() EnterBreakState(debuggingSession); // change the source (rude edit since the base document content matches the PDB checksum, so the document is not out-of-sync): - solution = solution.WithDocumentText(document1.Id, SourceText.From("abstract class C { public abstract void M(); public abstract void N(); }")); + solution = solution.WithDocumentText(document1.Id, CreateText("abstract class C { public abstract void M(); public abstract void N(); }")); var document2 = solution.Projects.Single().Documents.Single(); // Rude Edits reported: @@ -1642,7 +1694,7 @@ public async Task RudeEdits_DelayLoadedModule() { var source1 = "class C { public void M() { } }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1); + var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); @@ -1650,7 +1702,7 @@ public async Task RudeEdits_DelayLoadedModule() var document1 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument("test.cs", SourceText.From(source1, Encoding.UTF8), filePath: sourceFile.Path); + AddDocument("test.cs", CreateText(source1), filePath: sourceFile.Path); var project = document1.Project; solution = project.Solution; @@ -1663,7 +1715,7 @@ public async Task RudeEdits_DelayLoadedModule() EnterBreakState(debuggingSession); // change the source (rude edit) before the library is loaded: - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C { public void M() { } }")); + solution = solution.WithDocumentText(document1.Id, CreateText("class C { public void M() { } }")); var document2 = solution.Projects.Single().Documents.Single(); // Rude Edits reported: @@ -1710,7 +1762,7 @@ public async Task SyntaxError() // change the source (compilation error): var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { ")); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M() { ")); var document2 = solution.Projects.Single().Documents.Single(); // compilation errors are not reported via EnC diagnostic analyzer: @@ -1750,7 +1802,7 @@ public async Task SemanticError() // change the source (compilation error): var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { int i = 0L; System.Console.WriteLine(i); } }", Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M() { int i = 0L; System.Console.WriteLine(i); } }")); var document2 = solution.Projects.Single().Documents.Single(); // compilation errors are not reported via EnC diagnostic analyzer: @@ -1810,7 +1862,7 @@ public async Task HasChanges() var oldSolution = solution; var projectC = solution.GetProjectsByName("C").Single(); var documentC = projectC.Documents.Single(d => d.Name == "C.cs"); - solution = solution.WithDocumentText(documentC.Id, SourceText.From("class C { void M() { ")); + solution = solution.WithDocumentText(documentC.Id, CreateText("class C { void M() { ")); Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); @@ -1902,8 +1954,8 @@ public async Task HasChanges_Documents(DocumentKind documentKind) var documentId = DocumentId.CreateNewId(projectId); solution = documentKind switch { - DocumentKind.Source => solution.AddDocument(documentId, "X", SourceText.From("xxx", Encoding.UTF8, SourceHashAlgorithms.Default), filePath: pathX), - DocumentKind.Additional => solution.AddAdditionalDocument(documentId, "X", SourceText.From("xxx", Encoding.UTF8, SourceHashAlgorithms.Default), filePath: pathX), + DocumentKind.Source => solution.AddDocument(documentId, "X", CreateText("xxx"), filePath: pathX), + DocumentKind.Additional => solution.AddAdditionalDocument(documentId, "X", CreateText("xxx"), filePath: pathX), DocumentKind.AnalyzerConfig => solution.AddAnalyzerConfigDocument(documentId, "X", GetAnalyzerConfigText(new[] { ("x", "1") }), filePath: pathX), _ => throw ExceptionUtilities.Unreachable(), }; @@ -1933,8 +1985,8 @@ public async Task HasChanges_Documents(DocumentKind documentKind) solution = documentKind switch { - DocumentKind.Source => solution.WithDocumentText(documentId, SourceText.From("xxx", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), - DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, SourceText.From("xxx", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), + DocumentKind.Source => solution.WithDocumentText(documentId, CreateText("xxx")), + DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, CreateText("xxx")), DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText(new[] { ("x", "1") })), _ => throw ExceptionUtilities.Unreachable(), }; @@ -1960,8 +2012,8 @@ public async Task HasChanges_Documents(DocumentKind documentKind) oldSolution = solution; solution = documentKind switch { - DocumentKind.Source => solution.WithDocumentText(documentId, SourceText.From("xxx-changed", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), - DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, SourceText.From("xxx-changed", Encoding.UTF8, checksumAlgorithm: SourceHashAlgorithms.Default)), + DocumentKind.Source => solution.WithDocumentText(documentId, CreateText("xxx-changed")), + DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, CreateText("xxx-changed")), DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText(new[] { ("x", "2") })), _ => throw ExceptionUtilities.Unreachable(), }; @@ -2013,8 +2065,8 @@ public async Task Project_Add() var sourceB3 = "class B { int F() => 2; }"; var dir = Temp.CreateDirectory(); - var sourceFileA = dir.CreateFile("a.cs").WriteAllText(sourceA1); - var sourceFileB = dir.CreateFile("b.cs").WriteAllText(sourceB1); + var sourceFileA = dir.CreateFile("a.cs").WriteAllText(sourceA1, Encoding.UTF8); + var sourceFileB = dir.CreateFile("b.cs").WriteAllText(sourceB1, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); solution = AddDefaultTestProject(solution, new[] { sourceA1 }); @@ -2026,8 +2078,8 @@ public async Task Project_Add() var debuggingSession = await StartDebuggingSessionAsync(service, solution); // An active statement may be present in the added file since the file exists in the PDB: - var activeLineSpanA1 = SourceText.From(sourceA1, Encoding.UTF8).Lines.GetLinePositionSpan(GetSpan(sourceA1, "System.Console.WriteLine(1);")); - var activeLineSpanB1 = SourceText.From(sourceB1, Encoding.UTF8).Lines.GetLinePositionSpan(GetSpan(sourceB1, "1")); + var activeLineSpanA1 = CreateText(sourceA1).Lines.GetLinePositionSpan(GetSpan(sourceA1, "System.Console.WriteLine(1);")); + var activeLineSpanB1 = CreateText(sourceB1).Lines.GetLinePositionSpan(GetSpan(sourceB1, "1")); var activeStatements = ImmutableArray.Create( new ManagedActiveStatementDebugInfo( @@ -2047,7 +2099,7 @@ public async Task Project_Add() var documentB2 = solution. AddProject("B", "B", LanguageNames.CSharp). - AddDocument("b.cs", SourceText.From(sourceB2, Encoding.UTF8, SourceHashAlgorithms.Default), filePath: sourceFileB.Path); + AddDocument("b.cs", CreateText(sourceB2), filePath: sourceFileB.Path); solution = documentB2.Project.Solution; @@ -2080,7 +2132,7 @@ public async Task Project_Add() Assert.Empty(diagnostics); // update document with a valid change: - solution = solution.WithDocumentText(documentB2.Id, SourceText.From(sourceB3, Encoding.UTF8, SourceHashAlgorithms.Default)); + solution = solution.WithDocumentText(documentB2.Id, CreateText(sourceB3)); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -2113,7 +2165,7 @@ public async Task Capabilities(bool breakState) var debuggingSession = await StartDebuggingSessionAsync(service, solution); // update document: - solution = solution.WithDocumentText(documentId, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(source2)); var diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetDocument(documentId), s_noActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics); @@ -2225,7 +2277,7 @@ int M() var debuggingSession = await StartDebuggingSessionAsync(service, solution); // change the source - solution = solution.WithDocumentText(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); // validate solution update status and emit var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -2261,7 +2313,7 @@ public async Task Capabilities_SynthesizedNewType() var debuggingSession = await StartDebuggingSessionAsync(service, solution); // update document: - solution = solution.WithDocumentText(documentId, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(source2)); var document2 = solution.Projects.Single().Documents.Single(); // These errors aren't reported as document diagnostics @@ -2294,7 +2346,7 @@ public async Task ValidSignificantChange_EmitError() // change the source (valid edit but passing no encoding to emulate emit error): var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", encoding: null)); + solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", encoding: null, SourceHashAlgorithms.Default)); var document2 = solution.Projects.Single().Documents.Single(); var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noActiveSpans, CancellationToken.None); @@ -2352,7 +2404,7 @@ public async Task ValidSignificantChange_ApplyBeforeFileWatcherEvent(bool saveDo var source1 = "class C1 { void M() { System.Console.WriteLine(1); } }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("test.cs").WriteAllText(source1); + var sourceFile = dir.CreateFile("test.cs").WriteAllText(source1, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); @@ -2360,27 +2412,38 @@ public async Task ValidSignificantChange_ApplyBeforeFileWatcherEvent(bool saveDo var document1 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(DefaultTargetFramework)). - AddDocument("test.cs", SourceText.From("class C1 { void M() { System.Console.WriteLine(0); } }", Encoding.UTF8), filePath: sourceFile.Path); + AddDocument("test.cs", CreateText("class C1 { void M() { System.Console.WriteLine(0); } }"), filePath: sourceFile.Path); var documentId = document1.Id; solution = document1.Project.Solution; + var sourceTextProvider = new MockPdbMatchingSourceTextProvider() + { + TryGetMatchingSourceTextImpl = (filePath, requiredChecksum, checksumAlgorithm) => + { + Assert.Equal(sourceFile.Path, filePath); + AssertEx.Equal(requiredChecksum, CreateText(source1).GetChecksum()); + Assert.Equal(SourceHashAlgorithms.Default, checksumAlgorithm); + + return source1; + } + }; + var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); - var debuggingSession = await StartDebuggingSessionAsync(service, solution, initialState: CommittedSolution.DocumentState.None); + var debuggingSession = await StartDebuggingSessionAsync(service, solution, initialState: CommittedSolution.DocumentState.None, sourceTextProvider); EnterBreakState(debuggingSession); // The user opens the source file and changes the source before Roslyn receives file watcher event. var source2 = "class C1 { void M() { System.Console.WriteLine(2); } }"; - solution = solution.WithDocumentText(documentId, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(source2)); var document2 = solution.GetDocument(documentId); // Save the document: if (saveDocument) { - await debuggingSession.OnSourceFileUpdatedAsync(document2); - sourceFile.WriteAllText(source2); + sourceFile.WriteAllText(source2, Encoding.UTF8); } // EnC service queries for a document, which triggers read of the source file from disk. @@ -2395,7 +2458,7 @@ public async Task ValidSignificantChange_ApplyBeforeFileWatcherEvent(bool saveDo EnterBreakState(debuggingSession); // file watcher updates the workspace: - solution = solution.WithDocumentText(documentId, CreateSourceTextFromFile(sourceFile.Path)); + solution = solution.WithDocumentText(documentId, CreateTextFromFile(sourceFile.Path)); var document3 = solution.Projects.Single().Documents.Single(); (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -2429,7 +2492,7 @@ public async Task ValidSignificantChange_FileUpdateNotObservedBeforeDebuggingSes var source3 = "class C1 { void M() { System.Console.WriteLine(3); } }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("test.cs").WriteAllText(source2); + var sourceFile = dir.CreateFile("test.cs").WriteAllText(source2, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); @@ -2437,7 +2500,7 @@ public async Task ValidSignificantChange_FileUpdateNotObservedBeforeDebuggingSes var document2 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument("test.cs", SourceText.From(source2, Encoding.UTF8), filePath: sourceFile.Path); + AddDocument("test.cs", CreateText(source2), filePath: sourceFile.Path); var documentId = document2.Id; @@ -2451,7 +2514,7 @@ public async Task ValidSignificantChange_FileUpdateNotObservedBeforeDebuggingSes EnterBreakState(debuggingSession); // user edits the file: - solution = solution.WithDocumentText(documentId, SourceText.From(source3, Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(source3)); var document3 = solution.Projects.Single().Documents.Single(); // EnC service queries for a document, but the source file on disk doesn't match the PDB @@ -2466,16 +2529,16 @@ public async Task ValidSignificantChange_FileUpdateNotObservedBeforeDebuggingSes AssertEx.Equal(new[] { $"{project.Id}: Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}" }, InspectDiagnostics(emitDiagnostics)); // undo: - solution = solution.WithDocumentText(documentId, SourceText.From(source1, Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(source1)); var currentDocument = solution.GetDocument(documentId); // save (note that this call will fail to match the content with the PDB since it uses the content prior to the actual file write) - await debuggingSession.OnSourceFileUpdatedAsync(currentDocument); + // TODO: await debuggingSession.OnSourceFileUpdatedAsync(currentDocument); var (doc, state) = await debuggingSession.LastCommittedSolution.GetDocumentAndStateAsync(documentId, currentDocument, CancellationToken.None); Assert.Null(doc); Assert.Equal(CommittedSolution.DocumentState.OutOfSync, state); - sourceFile.WriteAllText(source1); + sourceFile.WriteAllText(source1, Encoding.UTF8); (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); @@ -2497,7 +2560,7 @@ public async Task ValidSignificantChange_AddedFileNotObservedBeforeDebuggingSess var source1 = "class C1 { void M() { System.Console.WriteLine(1); } }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("test.cs").WriteAllText(source1); + var sourceFile = dir.CreateFile("test.cs").WriteAllText(source1, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); @@ -2517,7 +2580,7 @@ public async Task ValidSignificantChange_AddedFileNotObservedBeforeDebuggingSess // An active statement may be present in the added file since the file exists in the PDB: var activeInstruction1 = new ManagedInstructionId(new ManagedMethodId(moduleId, token: 0x06000001, version: 1), ilOffset: 1); var activeSpan1 = GetSpan(source1, "System.Console.WriteLine(1);"); - var sourceText1 = SourceText.From(source1, Encoding.UTF8); + var sourceText1 = CreateText(source1); var activeLineSpan1 = sourceText1.Lines.GetLinePositionSpan(activeSpan1); var activeStatements = ImmutableArray.Create( new ManagedActiveStatementDebugInfo( @@ -2559,7 +2622,7 @@ public async Task ValidSignificantChange_DocumentOutOfSync(bool delayLoad) var sourceOnDisk = "class C1 { void M() { System.Console.WriteLine(1); } }"; var dir = Temp.CreateDirectory(); - var sourceFile = dir.CreateFile("test.cs").WriteAllText(sourceOnDisk); + var sourceFile = dir.CreateFile("test.cs").WriteAllText(sourceOnDisk, Encoding.UTF8); using var _ = CreateWorkspace(out var solution, out var service); @@ -2567,7 +2630,7 @@ public async Task ValidSignificantChange_DocumentOutOfSync(bool delayLoad) var document1 = solution. AddProject("test", "test", LanguageNames.CSharp). AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument("test.cs", SourceText.From("class C1 { void M() { System.Console.WriteLine(0); } }", Encoding.UTF8), filePath: sourceFile.Path); + AddDocument("test.cs", CreateText("class C1 { void M() { System.Console.WriteLine(0); } }"), filePath: sourceFile.Path); var project = document1.Project; solution = project.Solution; @@ -2590,7 +2653,7 @@ public async Task ValidSignificantChange_DocumentOutOfSync(bool delayLoad) Assert.Empty(emitDiagnostics); // a file watcher observed a change and updated the document, so it now reflects the content on disk (the code that we compiled): - solution = solution.WithDocumentText(document1.Id, SourceText.From(sourceOnDisk, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(sourceOnDisk)); var document3 = solution.Projects.Single().Documents.Single(); var diagnostics = await service.GetDocumentDiagnosticsAsync(document3, s_noActiveSpans, CancellationToken.None); @@ -2626,7 +2689,7 @@ public async Task ValidSignificantChange_EmitSuccessful(bool breakMode, bool com } // change the source (valid edit): - solution = solution.WithDocumentText(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); var document2 = solution.GetDocument(document1.Id); var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noActiveSpans, CancellationToken.None); @@ -2767,7 +2830,7 @@ public async Task ValidSignificantChange_EmitSuccessful_UpdateDeferred(bool comm EnterBreakState(debuggingSession, activeStatements); // change the source (valid edit): - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M1() { int a = 1; System.Console.WriteLine(a); } void M2() { System.Console.WriteLine(2); } }", Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M1() { int a = 1; System.Console.WriteLine(a); } void M2() { System.Console.WriteLine(2); } }")); var document2 = solution.GetDocument(document1.Id); // validate solution update status and emit: @@ -2820,7 +2883,7 @@ public async Task ValidSignificantChange_EmitSuccessful_UpdateDeferred(bool comm // Since the method hasn't been edited before we'll read the baseline PDB to get the signature token. // This validates that the Portable PDB reader can be used (and is not disposed) for a second generation edit. var document3 = solution.GetDocument(document1.Id); - solution = solution.WithDocumentText(document3.Id, SourceText.From("class C1 { void M1() { int a = 3; System.Console.WriteLine(a); } void M2() { System.Console.WriteLine(2); } }", Encoding.UTF8)); + solution = solution.WithDocumentText(document3.Id, CreateText("class C1 { void M1() { int a = 3; System.Console.WriteLine(a); } void M2() { System.Console.WriteLine(2); } }")); (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Equal(ModuleUpdateStatus.Ready, updates.Status); @@ -2882,8 +2945,8 @@ partial class C { int Y = 2; } // change the source (valid edit): var documentA = project.Documents.First(); var documentB = project.Documents.Skip(1).First(); - solution = solution.WithDocumentText(documentA.Id, SourceText.From(sourceA2, Encoding.UTF8)); - solution = solution.WithDocumentText(documentB.Id, SourceText.From(sourceB2, Encoding.UTF8)); + solution = solution.WithDocumentText(documentA.Id, CreateText(sourceA2)); + solution = solution.WithDocumentText(documentB.Id, CreateText(sourceB2)); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -2930,7 +2993,7 @@ class C { int Y => 2; } EnterBreakState(debuggingSession); // change the source (valid edit) - solution = solution.WithDocumentText(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -2990,7 +3053,7 @@ int M() EnterBreakState(debuggingSession); // change the source (valid edit): - solution = solution.WithDocumentText(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -3038,7 +3101,7 @@ partial class C { int X = 1; } EnterBreakState(debuggingSession); // change the source (valid edit): - solution = solution.WithDocumentText(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -3086,7 +3149,7 @@ class C { int Y => 1; } // change the additional source (valid edit): var additionalDocument1 = solution.Projects.Single().AdditionalDocuments.Single(); - solution = solution.WithAdditionalDocumentText(additionalDocument1.Id, SourceText.From(additionalSourceV2, Encoding.UTF8)); + solution = solution.WithAdditionalDocumentText(additionalDocument1.Id, CreateText(additionalSourceV2)); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -3212,7 +3275,7 @@ public async Task RudeEdit() var debuggingSession = await StartDebuggingSessionAsync(service, solution); // update document: - solution = solution.WithDocumentText(documentId, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(source2)); var document2 = solution.Projects.Single().Documents.Single(); // These errors aren't reported as document diagnostics @@ -3282,8 +3345,8 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() // First update. // - solution = solution.WithDocumentText(projectA.Documents.Single().Id, SourceText.From(source2, Encoding.UTF8)); - solution = solution.WithDocumentText(projectB.Documents.Single().Id, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(projectA.Documents.Single().Id, CreateText(source2)); + solution = solution.WithDocumentText(projectB.Documents.Single().Id, CreateText(source2)); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -3330,8 +3393,8 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() // Second update. // - solution = solution.WithDocumentText(projectA.Documents.Single().Id, SourceText.From(source3, Encoding.UTF8)); - solution = solution.WithDocumentText(projectB.Documents.Single().Id, SourceText.From(source3, Encoding.UTF8)); + solution = solution.WithDocumentText(projectA.Documents.Single().Id, CreateText(source3)); + solution = solution.WithDocumentText(projectB.Documents.Single().Id, CreateText(source3)); // validate solution update status and emit: (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -3402,7 +3465,7 @@ public async Task ValidSignificantChange_BaselineCreationFailed_NoStream() EnterBreakState(debuggingSession); // change the source (valid edit): - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M() { System.Console.WriteLine(2); } }")); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); AssertEx.Equal(new[] { $"{document1.Project.Id}: Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "test-pdb", new FileNotFoundException().Message)}" }, InspectDiagnostics(emitDiagnostics)); @@ -3435,7 +3498,7 @@ public async Task ValidSignificantChange_BaselineCreationFailed_AssemblyReadErro // change the source (valid edit): var document1 = solution.Projects.Single().Documents.Single(); - solution = solution.WithDocumentText(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText("class C1 { void M() { System.Console.WriteLine(2); } }")); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); AssertEx.Equal(new[] { $"{document.Project.Id}: Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "test-assembly", "*message*")}" }, InspectDiagnostics(emitDiagnostics)); @@ -3471,7 +3534,7 @@ public async Task ActiveStatements() var documentPath = document1.FilePath; var sourceTextV1 = document1.GetTextSynchronously(CancellationToken.None); - var sourceTextV2 = SourceText.From(sourceV2, Encoding.UTF8); + var sourceTextV2 = CreateText(sourceV2); var activeLineSpan11 = sourceTextV1.Lines.GetLinePositionSpan(activeSpan11); var activeLineSpan12 = sourceTextV1.Lines.GetLinePositionSpan(activeSpan12); @@ -3564,7 +3627,7 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy var documentFilePath = document1.FilePath; var sourceTextV1 = await document1.GetTextAsync(CancellationToken.None); - var sourceTextV2 = SourceText.From(sourceV2, Encoding.UTF8); + var sourceTextV2 = CreateText(sourceV2); var activeLineSpan11 = sourceTextV1.Lines.GetLinePositionSpan(activeSpan11); var activeLineSpan12 = sourceTextV1.Lines.GetLinePositionSpan(activeSpan12); @@ -3631,7 +3694,7 @@ public async Task ActiveStatements_ForeignDocument(bool withPath, bool designTim var project = solution.AddProject("dummy_proj", "dummy_proj", designTimeOnly ? LanguageNames.CSharp : NoCompilationConstants.LanguageName); var filePath = withPath ? Path.Combine(TempRoot.Root, "test.cs") : null; - var sourceText = SourceText.From("dummy1", Encoding.UTF8, SourceHashAlgorithms.Default); + var sourceText = CreateText("dummy1"); var documentInfo = DocumentInfo.Create( DocumentId.CreateNewId(project.Id, "test"), @@ -3663,7 +3726,7 @@ public async Task ActiveStatements_ForeignDocument(bool withPath, bool designTim Assert.Empty(baseSpans.Single()); // update solution: - solution = solution.WithDocumentText(document.Id, SourceText.From("dummy2")); + solution = solution.WithDocumentText(document.Id, CreateText("dummy2")); baseSpans = await debuggingSession.GetBaseActiveStatementSpansAsync(solution, ImmutableArray.Create(document.Id), CancellationToken.None); Assert.Empty(baseSpans.Single()); @@ -3822,7 +3885,7 @@ static void M() EnterBreakState(debuggingSession, debugInfos); // update document to test a changed solution - solution = solution.WithDocumentText(document.Id, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(document.Id, CreateText(source2)); document = solution.GetDocument(document.Id); var baseActiveStatementMap = await debuggingSession.EditSession.BaseActiveStatements.GetValueAsync(CancellationToken.None).ConfigureAwait(false); @@ -3920,7 +3983,7 @@ void F() })); // change the source (valid edit) - solution = solution.WithDocumentText(document1.Id, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(document1.Id, CreateText(source2)); // validate solution update status and emit: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -4002,7 +4065,7 @@ int F() })); // change the source (rude edit) - solution = solution.WithDocumentText(document.Id, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(document.Id, CreateText(source2)); document = solution.GetDocument(document.Id); var diagnostics = await service.GetDocumentDiagnosticsAsync(document, s_noActiveSpans, CancellationToken.None); @@ -4014,13 +4077,13 @@ int F() Assert.Equal(ModuleUpdateStatus.RestartRequired, updates.Status); // undo the change - solution = solution.WithDocumentText(document.Id, SourceText.From(source1, Encoding.UTF8)); + solution = solution.WithDocumentText(document.Id, CreateText(source1)); document = solution.GetDocument(document.Id); ExitBreakState(debuggingSession, ImmutableArray.Create(document.Id)); // change the source (now a valid edit since there is no active statement) - solution = solution.WithDocumentText(document.Id, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(document.Id, CreateText(source2)); diagnostics = await service.GetDocumentDiagnosticsAsync(document, s_noActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); @@ -4088,7 +4151,7 @@ static void F() ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, // F })); - solution = solution.WithDocumentText(documentId, SourceText.From(ActiveStatementsDescription.ClearTags(markedSourceV2), Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(ActiveStatementsDescription.ClearTags(markedSourceV2))); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); @@ -4108,7 +4171,7 @@ static void F() // Hot Reload update F v2 -> v3 - solution = solution.WithDocumentText(documentId, SourceText.From(ActiveStatementsDescription.ClearTags(markedSourceV3), Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(ActiveStatementsDescription.ClearTags(markedSourceV3))); (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); @@ -4144,7 +4207,7 @@ static void F() new ActiveStatementSpan(0, new LinePositionSpan(new(4,41), new(4,42)), ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, unmappedDocumentId: null), }, spans); - solution = solution.WithDocumentText(documentId, SourceText.From(ActiveStatementsDescription.ClearTags(markedSourceV4), Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(ActiveStatementsDescription.ClearTags(markedSourceV4))); (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); @@ -4221,7 +4284,7 @@ static void F() // Update to snapshot 2, but don't apply - solution = solution.WithDocumentText(documentId, SourceText.From(ActiveStatementsDescription.ClearTags(markedSource2), Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(ActiveStatementsDescription.ClearTags(markedSource2))); // EnC update F v2 -> v3 @@ -4251,7 +4314,7 @@ static void F() new ActiveStatementSpan(1, expectedSpanF1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, documentId) }, spans); - solution = solution.WithDocumentText(documentId, SourceText.From(ActiveStatementsDescription.ClearTags(markedSource3), Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(ActiveStatementsDescription.ClearTags(markedSource3))); // check that the active statement is mapped correctly to snapshot v3: var expectedSpanG2 = new LinePositionSpan(new LinePosition(3, 41), new LinePosition(3, 42)); @@ -4330,7 +4393,7 @@ static void F() // Apply update: F v1 -> v2. - solution = solution.WithDocumentText(documentId, SourceText.From(ActiveStatementsDescription.ClearTags(markedSource2), Encoding.UTF8)); + solution = solution.WithDocumentText(documentId, CreateText(ActiveStatementsDescription.ClearTags(markedSource2))); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); @@ -4383,10 +4446,10 @@ public async Task MultiSession() var source3 = "class C { void M() { WriteLine(2); } }"; var dir = Temp.CreateDirectory(); - var sourceFileA = dir.CreateFile("A.cs").WriteAllText(source1); - var moduleId = EmitLibrary(source1, sourceFileA.Path, Encoding.UTF8, "Proj"); + var sourceFileA = dir.CreateFile("A.cs").WriteAllText(source1, Encoding.UTF8); + var moduleId = EmitLibrary(source1, sourceFileA.Path, assemblyName: "Proj"); - using var workspace = CreateWorkspace(out var solution, out var encService); + using var _ = CreateWorkspace(out var solution, out var encService); var projectP = solution. AddProject("P", "P", LanguageNames.CSharp). @@ -4406,19 +4469,20 @@ public async Task MultiSession() var sessionId = await encService.StartDebuggingSessionAsync( solution, _debuggerService, + NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments: ImmutableArray.Empty, captureAllMatchingDocuments: true, reportDiagnostics: true, CancellationToken.None); - var solution1 = solution.WithDocumentText(documentIdA, SourceText.From("class C { void M() { System.Console.WriteLine(" + i + "); } }", Encoding.UTF8)); + var solution1 = solution.WithDocumentText(documentIdA, CreateText("class C { void M() { System.Console.WriteLine(" + i + "); } }")); var result1 = await encService.EmitSolutionUpdateAsync(sessionId, solution1, s_noActiveSpans, CancellationToken.None); Assert.Empty(result1.Diagnostics); Assert.Equal(1, result1.ModuleUpdates.Updates.Length); encService.DiscardSolutionUpdate(sessionId); - var solution2 = solution1.WithDocumentText(documentIdA, SourceText.From(source3, Encoding.UTF8)); + var solution2 = solution1.WithDocumentText(documentIdA, CreateText(source3)); var result2 = await encService.EmitSolutionUpdateAsync(sessionId, solution2, s_noActiveSpans, CancellationToken.None); Assert.Equal("CS0103", result2.Diagnostics.Single().Diagnostics.Single().Id); @@ -4468,8 +4532,8 @@ public async Task WatchHotReloadServiceTest() var source4 = "class C { void M() { System.Console.WriteLine(2)/* missing semicolon */ }"; var dir = Temp.CreateDirectory(); - var sourceFileA = dir.CreateFile("A.cs").WriteAllText(source1); - var moduleId = EmitLibrary(source1, sourceFileA.Path, Encoding.UTF8, "Proj"); + var sourceFileA = dir.CreateFile("A.cs").WriteAllText(source1, Encoding.UTF8); + var moduleId = EmitLibrary(source1, sourceFileA.Path, assemblyName: "Proj"); using var workspace = CreateWorkspace(out var solution, out var encService); @@ -4500,7 +4564,7 @@ public async Task WatchHotReloadServiceTest() }, matchingDocuments.Select(e => (solution.GetDocument(e.id).Name, e.state)).OrderBy(e => e.Name).Select(e => e.ToString())); // Valid update: - solution = solution.WithDocumentText(documentIdA, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(documentIdA, CreateText(source2)); var result = await hotReload.EmitSolutionUpdateAsync(solution, CancellationToken.None); Assert.Empty(result.diagnostics); @@ -4508,7 +4572,7 @@ public async Task WatchHotReloadServiceTest() AssertEx.Equal(new[] { 0x02000002 }, result.updates[0].UpdatedTypes); // Rude edit: - solution = solution.WithDocumentText(documentIdA, SourceText.From(source3, Encoding.UTF8)); + solution = solution.WithDocumentText(documentIdA, CreateText(source3)); result = await hotReload.EmitSolutionUpdateAsync(solution, CancellationToken.None); AssertEx.Equal( @@ -4518,7 +4582,7 @@ public async Task WatchHotReloadServiceTest() Assert.Empty(result.updates); // Syntax error (not reported in diagnostics): - solution = solution.WithDocumentText(documentIdA, SourceText.From(source4, Encoding.UTF8)); + solution = solution.WithDocumentText(documentIdA, CreateText(source4)); result = await hotReload.EmitSolutionUpdateAsync(solution, CancellationToken.None); Assert.Empty(result.diagnostics); @@ -4536,8 +4600,8 @@ public async Task UnitTestingHotReloadServiceTest() var source4 = "class C { void M() { System.Console.WriteLine(2)/* missing semicolon */ }"; var dir = Temp.CreateDirectory(); - var sourceFileA = dir.CreateFile("A.cs").WriteAllText(source1); - var moduleId = EmitLibrary(source1, sourceFileA.Path, Encoding.UTF8, "Proj"); + var sourceFileA = dir.CreateFile("A.cs").WriteAllText(source1, Encoding.UTF8); + var moduleId = EmitLibrary(source1, sourceFileA.Path, assemblyName: "Proj"); using var workspace = CreateWorkspace(out var solution, out var encService); @@ -4567,13 +4631,13 @@ public async Task UnitTestingHotReloadServiceTest() }, matchingDocuments.Select(e => (solution.GetDocument(e.id).Name, e.state)).OrderBy(e => e.Name).Select(e => e.ToString())); // Valid change - solution = solution.WithDocumentText(documentIdA, SourceText.From(source2, Encoding.UTF8)); + solution = solution.WithDocumentText(documentIdA, CreateText(source2)); var result = await hotReload.EmitSolutionUpdateAsync(solution, commitUpdates: true, CancellationToken.None); Assert.Empty(result.diagnostics); Assert.Equal(1, result.updates.Length); - solution = solution.WithDocumentText(documentIdA, SourceText.From(source3, Encoding.UTF8)); + solution = solution.WithDocumentText(documentIdA, CreateText(source3)); // Rude edit result = await hotReload.EmitSolutionUpdateAsync(solution, commitUpdates: true, CancellationToken.None); @@ -4584,7 +4648,7 @@ public async Task UnitTestingHotReloadServiceTest() Assert.Empty(result.updates); // Syntax error is reported in the diagnostics: - solution = solution.WithDocumentText(documentIdA, SourceText.From(source4, Encoding.UTF8)); + solution = solution.WithDocumentText(documentIdA, CreateText(source4)); result = await hotReload.EmitSolutionUpdateAsync(solution, commitUpdates: true, CancellationToken.None); Assert.Equal(1, result.diagnostics.Length); @@ -4592,5 +4656,65 @@ public async Task UnitTestingHotReloadServiceTest() hotReload.EndSession(); } + + [Fact] + public async Task DefaultPdbMatchingSourceTextProvider() + { + var source1 = "class C1 { void M() { System.Console.WriteLine(\"a\"); } }"; + var source2 = "class C1 { void M() { System.Console.WriteLine(\"b\"); } }"; + var source3 = "class C1 { void M() { System.Console.WriteLine(\"c\"); } }"; + + var dir = Temp.CreateDirectory(); + var sourceFile = dir.CreateFile("test.cs").WriteAllText(source1, Encoding.UTF8); + + using var workspace = CreateEditorWorkspace(out var solution, out var service, out var languageService); + var sourceTextProvider = workspace.GetService(); + + var projectId = ProjectId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); + + solution = solution. + AddProject(projectId, "test", "test", LanguageNames.CSharp). + WithProjectChecksumAlgorithm(projectId, SourceHashAlgorithms.Default). + AddMetadataReferences(projectId, TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). + AddDocument(DocumentInfo.Create( + documentId, + name: "test.cs", + loader: new WorkspaceFileTextLoader(workspace.Services.SolutionServices, sourceFile.Path, Encoding.UTF8), + filePath: sourceFile.Path)); + + Assert.True(workspace.SetCurrentSolution(_ => solution, WorkspaceChangeKind.SolutionAdded)); + solution = workspace.CurrentSolution; + + var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); + + // hydrate document text and overwrite file content: + var document1 = await solution.GetDocument(documentId).GetTextAsync(); + File.WriteAllText(sourceFile.Path, source2, Encoding.UTF8); + + await languageService.StartSessionAsync(CancellationToken.None); + await languageService.EnterBreakStateAsync(CancellationToken.None); + + workspace.OnDocumentOpened(documentId, new TestSourceTextContainer() + { + Text = SourceText.From(source3, Encoding.UTF8, SourceHashAlgorithm.Sha1) + }); + + await workspace.GetService().GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); + + var (key, (documentState, version)) = sourceTextProvider.GetTestAccessor().GetDocumentsWithChangedLoaderByPath().Single(); + Assert.Equal(sourceFile.Path, key); + Assert.Equal(solution.WorkspaceVersion, version); + Assert.Equal(source1, (await documentState.GetTextAsync(CancellationToken.None)).ToString()); + + // check committed document status: + var debuggingSession = service.GetTestAccessor().GetActiveDebuggingSessions().Single(); + var (document, state) = await debuggingSession.LastCommittedSolution.GetDocumentAndStateAsync(documentId, currentDocument: null, CancellationToken.None); + var text = await document.GetTextAsync(); + Assert.Equal(CommittedSolution.DocumentState.MatchesBuildOutput, state); + Assert.Equal(source1, (await document.GetTextAsync(CancellationToken.None)).ToString()); + + await languageService.EndSessionAsync(CancellationToken.None); + } } } diff --git a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs index 7be7c2b568a7c..3f2f50ddd7f34 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs @@ -52,6 +52,7 @@ private static EditSession CreateEditSession( solution, mockDebuggerService, mockCompilationOutputsProvider, + NullPdbMatchingSourceTextProvider.Instance, SpecializedCollections.EmptyEnumerable>(), reportDiagnostics: true); diff --git a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockHostWorkspaceProvider.cs b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockHostWorkspaceProvider.cs new file mode 100644 index 0000000000000..bc8834827ef5d --- /dev/null +++ b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockHostWorkspaceProvider.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Composition; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests; + +[Export(typeof(IHostWorkspaceProvider)), PartNotDiscoverable, Shared] +internal class MockHostWorkspaceProvider : IHostWorkspaceProvider +{ + public Workspace Workspace { get; set; } = null!; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public MockHostWorkspaceProvider() + { + } +} diff --git a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockManagedHotReloadService.cs b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockManagedHotReloadService.cs new file mode 100644 index 0000000000000..20efc9161d74f --- /dev/null +++ b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockManagedHotReloadService.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; +using Microsoft.VisualStudio.Debugger.Contracts.HotReload; + +namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests; + +[Export(typeof(IManagedHotReloadService)), PartNotDiscoverable, Shared] +internal class MockManagedHotReloadService : IManagedHotReloadService +{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public MockManagedHotReloadService() + { + } + + public ValueTask> GetActiveStatementsAsync(CancellationToken cancellation) + => throw new NotImplementedException(); + + public ValueTask GetAvailabilityAsync(Guid module, CancellationToken cancellation) + => throw new NotImplementedException(); + + public ValueTask> GetCapabilitiesAsync(CancellationToken cancellation) + => throw new NotImplementedException(); + + public ValueTask PrepareModuleForUpdateAsync(Guid module, CancellationToken cancellation) + => throw new NotImplementedException(); +} diff --git a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockPdbMatchingSourceTextProvider.cs b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockPdbMatchingSourceTextProvider.cs new file mode 100644 index 0000000000000..b9161fd745c5b --- /dev/null +++ b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockPdbMatchingSourceTextProvider.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests; + +internal class MockPdbMatchingSourceTextProvider : IPdbMatchingSourceTextProvider +{ + public Func, SourceHashAlgorithm, string?>? TryGetMatchingSourceTextImpl; + + public ValueTask TryGetMatchingSourceTextAsync(string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + => ValueTaskFactory.FromResult(TryGetMatchingSourceTextImpl?.Invoke(filePath, requiredChecksum, checksumAlgorithm)); +} diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 1471cb90cc07a..78d76769d3847 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -145,7 +145,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) IManagedHotReloadService? remoteDebuggeeModuleMetadataProvider = null; - var debuggingSession = mockEncService.StartDebuggingSessionImpl = (solution, debuggerService, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics) => + var debuggingSession = mockEncService.StartDebuggingSessionImpl = (solution, debuggerService, sourceTextProvider, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics) => { Assert.Equal("proj", solution.GetRequiredProject(projectId).Name); AssertEx.Equal(new[] { documentId }, captureMatchingDocuments); @@ -163,6 +163,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) IsEditAndContinueAvailable = _ => new ManagedHotReloadAvailability(ManagedHotReloadAvailabilityStatus.NotAllowedForModule, "can't do enc"), GetActiveStatementsImpl = () => ImmutableArray.Create(as1) }, + sourceTextProvider: NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments: ImmutableArray.Create(documentId), captureAllMatchingDocuments: false, reportDiagnostics: true, @@ -347,21 +348,6 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) Assert.Empty(await proxy.GetDocumentDiagnosticsAsync(inProcOnlyDocument, inProcOnlyDocument, activeStatementSpanProvider, CancellationToken.None)); Assert.Equal(diagnostic.GetMessage(), (await proxy.GetDocumentDiagnosticsAsync(document, document, activeStatementSpanProvider, CancellationToken.None)).Single().GetMessage()); - // OnSourceFileUpdatedAsync - - called = false; - mockEncService.OnSourceFileUpdatedImpl = updatedDocument => - { - Assert.Equal(documentId, updatedDocument.Id); - called = true; - }; - - await proxy.OnSourceFileUpdatedAsync(inProcOnlyDocument, CancellationToken.None); - Assert.False(called); - - await proxy.OnSourceFileUpdatedAsync(document, CancellationToken.None); - Assert.True(called); - // EndDebuggingSession mockEncService.EndDebuggingSessionImpl = (out ImmutableArray documentsToReanalyze) => diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs index 77e162c6c282d..1abcb6c9d3cc4 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs @@ -23,7 +23,7 @@ internal class MockEditAndContinueWorkspaceService : IEditAndContinueWorkspaceSe public Func? GetCurrentActiveStatementPositionImpl; public Func>? GetAdjustedActiveStatementSpansImpl; - public Func, bool, bool, DebuggingSessionId>? StartDebuggingSessionImpl; + public Func, bool, bool, DebuggingSessionId>? StartDebuggingSessionImpl; public ActionOut>? EndDebuggingSessionImpl; public Func? EmitSolutionUpdateImpl; @@ -82,7 +82,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Documen public void OnSourceFileUpdated(Document document) => OnSourceFileUpdatedImpl?.Invoke(document); - public ValueTask StartDebuggingSessionAsync(Solution solution, IManagedHotReloadService debuggerService, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken) - => new((StartDebuggingSessionImpl ?? throw new NotImplementedException()).Invoke(solution, debuggerService, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics)); + public ValueTask StartDebuggingSessionAsync(Solution solution, IManagedHotReloadService debuggerService, IPdbMatchingSourceTextProvider sourceTextProvider, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken) + => new((StartDebuggingSessionImpl ?? throw new NotImplementedException()).Invoke(solution, debuggerService, sourceTextProvider, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics)); } } diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index a12e6b30080de..66ef46bdb0d75 100644 --- a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs +++ b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs @@ -131,17 +131,6 @@ public ImmutableArray GetDocumentIdsWithFilePath(string path) public bool ContainsDocument(DocumentId documentId) => _solution.ContainsDocument(documentId); - /// - /// Observes the content of the specified document checks if it matches the PDB. - /// - /// - /// When the is started we check the content of all open documents against the PDB. - /// Then we check the content whenever another document is opened. This approach gives us the opportunity to record the - /// document content state before the user has a chance to edit the files. - /// - public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancellationToken) - => GetDocumentAndStateAsync(document.Id, document, cancellationToken, reloadOutOfSyncDocument: true); - /// /// Returns a document snapshot for given whose content exactly matches /// the source file used to compile the binary currently loaded in the debuggee. Returns null @@ -325,13 +314,13 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel var maybePdbHasDocument = TryReadSourceFileChecksumFromPdb(document, out var requiredChecksum, out var checksumAlgorithm); var maybeMatchingSourceText = (maybePdbHasDocument == true) ? - await TryGetMatchingSourceTextAsync(sourceText, document.FilePath, currentDocument, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) : default; + await TryGetMatchingSourceTextAsync(sourceText, document.FilePath, currentDocument, _debuggingSession.SourceTextProvider, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) : default; return (maybeMatchingSourceText, maybePdbHasDocument); } private static async ValueTask> TryGetMatchingSourceTextAsync( - SourceText sourceText, string filePath, Document? currentDocument, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + SourceText sourceText, string filePath, Document? currentDocument, IPdbMatchingSourceTextProvider sourceTextProvider, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { if (IsMatchingSourceText(sourceText, requiredChecksum, checksumAlgorithm)) { @@ -347,12 +336,19 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel } } + var text = await sourceTextProvider.TryGetMatchingSourceTextAsync(filePath, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false); + if (text != null) + { + return SourceText.From(text, sourceText.Encoding, checksumAlgorithm); + } + return await Task.Run(() => TryGetPdbMatchingSourceTextFromDisk(filePath, sourceText.Encoding, requiredChecksum, checksumAlgorithm), cancellationToken).ConfigureAwait(false); } internal static async Task>> GetMatchingDocumentsAsync( IEnumerable<(Project, IEnumerable)> documentsByProject, Func compilationOutputsProvider, + IPdbMatchingSourceTextProvider sourceTextProvider, CancellationToken cancellationToken) { var projectTasks = documentsByProject.Select(async projectDocumentStates => @@ -392,7 +388,7 @@ internal static async Task>> // TODO: https://github.com/dotnet/roslyn/issues/51993 // avoid rereading the file in common case - the workspace should create source texts with the right checksum algorithm and encoding if (TryReadSourceFileChecksumFromPdb(debugInfoReader, sourceFilePath, out var requiredChecksum, out var checksumAlgorithm) == true && - await TryGetMatchingSourceTextAsync(sourceText, sourceFilePath, currentDocument: null, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) is { HasValue: true, Value: not null }) + await TryGetMatchingSourceTextAsync(sourceText, sourceFilePath, currentDocument: null, sourceTextProvider, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) is { HasValue: true, Value: not null }) { return documentState.Id; } diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index 7011befae9a0e..cd6ab29199c4c 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -30,6 +30,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue internal sealed class DebuggingSession : IDisposable { private readonly Func _compilationOutputsProvider; + internal readonly IPdbMatchingSourceTextProvider SourceTextProvider; private readonly CancellationTokenSource _cancellationSource = new(); /// @@ -101,10 +102,12 @@ internal DebuggingSession( Solution solution, IManagedHotReloadService debuggerService, Func compilationOutputsProvider, + IPdbMatchingSourceTextProvider sourceTextProvider, IEnumerable> initialDocumentStates, bool reportDiagnostics) { _compilationOutputsProvider = compilationOutputsProvider; + SourceTextProvider = sourceTextProvider; _reportTelemetry = ReportTelemetry; _telemetry = new DebuggingSessionTelemetry(solution.State.SolutionAttributes.TelemetryId); @@ -153,9 +156,6 @@ internal void ThrowIfDisposed() throw new ObjectDisposedException(nameof(DebuggingSession)); } - internal Task OnSourceFileUpdatedAsync(Document document) - => LastCommittedSolution.OnSourceFileUpdatedAsync(document, _cancellationSource.Token); - private void StorePendingUpdate(Solution solution, SolutionUpdate update) { var previousPendingUpdate = Interlocked.Exchange(ref _pendingUpdate, new PendingSolutionUpdate( @@ -840,7 +840,8 @@ public async ValueTask> GetAdjustedActiveSta } } - public async ValueTask GetCurrentActiveStatementPositionAsync(Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, ManagedInstructionId instructionId, CancellationToken cancellationToken) + public async ValueTask GetCurrentActiveStatementPositionAsync( + Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, ManagedInstructionId instructionId, CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -991,7 +992,8 @@ public async ValueTask> GetAdjustedActiveSta Debug.Assert(oldProject.SupportsEditAndContinue()); Debug.Assert(newProject.SupportsEditAndContinue()); - documentId = await GetChangedDocumentContainingUnmappedActiveStatementAsync(activeStatementsMap, LastCommittedSolution, oldProject, newProject, baseActiveStatement, cancellationToken).ConfigureAwait(false); + documentId = await GetChangedDocumentContainingUnmappedActiveStatementAsync( + activeStatementsMap, LastCommittedSolution, oldProject, newProject, baseActiveStatement, cancellationToken).ConfigureAwait(false); } else { @@ -1042,7 +1044,8 @@ await GetChangedDocumentContainingUnmappedActiveStatementAsync( // Enumerate all changed documents in the project whose module contains the active statement. // For each such document enumerate all #line directives to find which maps code to the span that contains the active statement. - private static async ValueTask GetChangedDocumentContainingUnmappedActiveStatementAsync(ActiveStatementsMap baseActiveStatements, CommittedSolution oldSolution, Project oldProject, Project newProject, ActiveStatement activeStatement, CancellationToken cancellationToken) + private static async ValueTask GetChangedDocumentContainingUnmappedActiveStatementAsync( + ActiveStatementsMap baseActiveStatements, CommittedSolution oldSolution, Project oldProject, Project newProject, ActiveStatement activeStatement, CancellationToken cancellationToken) { Debug.Assert(oldProject.Id == newProject.Id); Debug.Assert(oldProject.SupportsEditAndContinue()); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index df0d65b4c7b36..c0e750dda2d11 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -94,19 +94,10 @@ private ImmutableArray GetDiagnosticReportingDebuggingSessions } } - public void OnSourceFileUpdated(Document document) - { - // notify all active debugging sessions - foreach (var debuggingSession in GetActiveDebuggingSessions()) - { - // fire and forget - _ = Task.Run(() => debuggingSession.OnSourceFileUpdatedAsync(document)).ReportNonFatalErrorAsync(); - } - } - public async ValueTask StartDebuggingSessionAsync( Solution solution, IManagedHotReloadService debuggerService, + IPdbMatchingSourceTextProvider sourceTextProvider, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, @@ -124,7 +115,7 @@ public async ValueTask StartDebuggingSessionAsync( solution.Projects.Select(project => (project, project.State.DocumentStates.States.Values)) : GetDocumentStatesGroupedByProject(solution, captureMatchingDocuments); - initialDocumentStates = await CommittedSolution.GetMatchingDocumentsAsync(documentsByProject, _compilationOutputsProvider, cancellationToken).ConfigureAwait(false); + initialDocumentStates = await CommittedSolution.GetMatchingDocumentsAsync(documentsByProject, _compilationOutputsProvider, sourceTextProvider, cancellationToken).ConfigureAwait(false); } else { @@ -132,7 +123,7 @@ public async ValueTask StartDebuggingSessionAsync( } var sessionId = new DebuggingSessionId(Interlocked.Increment(ref s_debuggingSessionId)); - var session = new DebuggingSession(sessionId, solution, debuggerService, _compilationOutputsProvider, initialDocumentStates, reportDiagnostics); + var session = new DebuggingSession(sessionId, solution, debuggerService, _compilationOutputsProvider, sourceTextProvider, initialDocumentStates, reportDiagnostics); lock (_debuggingSessions) { diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs index b6bc22c3c95cb..8bf325bd43e81 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs @@ -19,9 +19,7 @@ internal interface IEditAndContinueWorkspaceService : IWorkspaceService void CommitSolutionUpdate(DebuggingSessionId sessionId, out ImmutableArray documentsToReanalyze); void DiscardSolutionUpdate(DebuggingSessionId sessionId); - void OnSourceFileUpdated(Document document); - - ValueTask StartDebuggingSessionAsync(Solution solution, IManagedHotReloadService debuggerService, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken); + ValueTask StartDebuggingSessionAsync(Solution solution, IManagedHotReloadService debuggerService, IPdbMatchingSourceTextProvider sourceTextProvider, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken); void BreakStateOrCapabilitiesChanged(DebuggingSessionId sessionId, bool? inBreakState, out ImmutableArray documentsToReanalyze); void EndDebuggingSession(DebuggingSessionId sessionId, out ImmutableArray documentsToReanalyze); diff --git a/src/Features/Core/Portable/EditAndContinue/IPdbMatchingSourceTextProvider.cs b/src/Features/Core/Portable/EditAndContinue/IPdbMatchingSourceTextProvider.cs new file mode 100644 index 0000000000000..5dfea64de6476 --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/IPdbMatchingSourceTextProvider.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.EditAndContinue; + +internal interface IPdbMatchingSourceTextProvider +{ + // TODO: Return SourceText (https://github.com/dotnet/roslyn/issues/64504) or text changes (if we can maintain baseline) + ValueTask TryGetMatchingSourceTextAsync(string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken); +} + +internal sealed class NullPdbMatchingSourceTextProvider : IPdbMatchingSourceTextProvider +{ + public static readonly NullPdbMatchingSourceTextProvider Instance = new(); + + private NullPdbMatchingSourceTextProvider() + { + } + + public ValueTask TryGetMatchingSourceTextAsync(string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + => ValueTaskFactory.FromResult(null); +} diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs index cea8962d9938e..210d41dd456cc 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs @@ -23,6 +23,7 @@ internal interface ICallback ValueTask PrepareModuleForUpdateAsync(RemoteServiceCallbackId callbackId, Guid mvid, CancellationToken cancellationToken); ValueTask> GetSpansAsync(RemoteServiceCallbackId callbackId, DocumentId? documentId, string filePath, CancellationToken cancellationToken); + ValueTask TryGetMatchingSourceTextAsync(RemoteServiceCallbackId callbackId, string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken); } ValueTask> GetDocumentDiagnosticsAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DocumentId documentId, CancellationToken cancellationToken); @@ -50,6 +51,5 @@ internal interface ICallback ValueTask IsActiveStatementInExceptionRegionAsync(Checksum solutionChecksum, DebuggingSessionId sessionId, ManagedInstructionId instructionId, CancellationToken cancellationToken); ValueTask GetCurrentActiveStatementPositionAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, ManagedInstructionId instructionId, CancellationToken cancellationToken); - ValueTask OnSourceFileUpdatedAsync(Checksum solutionChecksum, DocumentId documentId, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs index 3343d33ec3694..7969b7d8d5ecf 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs @@ -37,26 +37,43 @@ public CallbackDispatcher() public ValueTask> GetSpansAsync(RemoteServiceCallbackId callbackId, DocumentId? documentId, string filePath, CancellationToken cancellationToken) => ((ActiveStatementSpanProviderCallback)GetCallback(callbackId)).GetSpansAsync(documentId, filePath, cancellationToken); + public ValueTask TryGetMatchingSourceTextAsync(RemoteServiceCallbackId callbackId, string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + => ((DebuggingSessionCallback)GetCallback(callbackId)).TryGetMatchingSourceTextAsync(filePath, requiredChecksum, checksumAlgorithm, cancellationToken); + public ValueTask> GetActiveStatementsAsync(RemoteServiceCallbackId callbackId, CancellationToken cancellationToken) - => ((EditSessionCallback)GetCallback(callbackId)).GetActiveStatementsAsync(cancellationToken); + => ((DebuggingSessionCallback)GetCallback(callbackId)).GetActiveStatementsAsync(cancellationToken); public ValueTask GetAvailabilityAsync(RemoteServiceCallbackId callbackId, Guid mvid, CancellationToken cancellationToken) - => ((EditSessionCallback)GetCallback(callbackId)).GetAvailabilityAsync(mvid, cancellationToken); + => ((DebuggingSessionCallback)GetCallback(callbackId)).GetAvailabilityAsync(mvid, cancellationToken); public ValueTask> GetCapabilitiesAsync(RemoteServiceCallbackId callbackId, CancellationToken cancellationToken) - => ((EditSessionCallback)GetCallback(callbackId)).GetCapabilitiesAsync(cancellationToken); + => ((DebuggingSessionCallback)GetCallback(callbackId)).GetCapabilitiesAsync(cancellationToken); public ValueTask PrepareModuleForUpdateAsync(RemoteServiceCallbackId callbackId, Guid mvid, CancellationToken cancellationToken) - => ((EditSessionCallback)GetCallback(callbackId)).PrepareModuleForUpdateAsync(mvid, cancellationToken); + => ((DebuggingSessionCallback)GetCallback(callbackId)).PrepareModuleForUpdateAsync(mvid, cancellationToken); } - private sealed class EditSessionCallback + private sealed class DebuggingSessionCallback { private readonly IManagedHotReloadService _debuggerService; + private readonly IPdbMatchingSourceTextProvider _sourceTextProvider; - public EditSessionCallback(IManagedHotReloadService debuggerService) + public DebuggingSessionCallback(IManagedHotReloadService debuggerService, IPdbMatchingSourceTextProvider sourceTextProvider) { _debuggerService = debuggerService; + _sourceTextProvider = sourceTextProvider; + } + + public async ValueTask TryGetMatchingSourceTextAsync(string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + { + try + { + return await _sourceTextProvider.TryGetMatchingSourceTextAsync(filePath, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + return null; + } } public async ValueTask> GetActiveStatementsAsync(CancellationToken cancellationToken) @@ -121,6 +138,7 @@ private IEditAndContinueWorkspaceService GetLocalService() public async ValueTask StartDebuggingSessionAsync( Solution solution, IManagedHotReloadService debuggerService, + IPdbMatchingSourceTextProvider sourceTextProvider, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, @@ -129,13 +147,13 @@ private IEditAndContinueWorkspaceService GetLocalService() var client = await RemoteHostClient.TryGetClientAsync(Workspace, cancellationToken).ConfigureAwait(false); if (client == null) { - var sessionId = await GetLocalService().StartDebuggingSessionAsync(solution, debuggerService, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics, cancellationToken).ConfigureAwait(false); + var sessionId = await GetLocalService().StartDebuggingSessionAsync(solution, debuggerService, sourceTextProvider, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics, cancellationToken).ConfigureAwait(false); return new RemoteDebuggingSessionProxy(Workspace, LocalConnection.Instance, sessionId); } - // need to keep the providers alive until the edit session ends: + // need to keep the providers alive until the session ends: var connection = client.CreateConnection( - callbackTarget: new EditSessionCallback(debuggerService)); + callbackTarget: new DebuggingSessionCallback(debuggerService, sourceTextProvider)); var sessionIdOpt = await connection.TryInvokeAsync( solution, @@ -224,27 +242,6 @@ private static Diagnostic RemapLocation(Document designTimeDocument, DiagnosticD return data.ToDiagnostic(location, ImmutableArray.Empty); } - public async ValueTask OnSourceFileUpdatedAsync(Document document, CancellationToken cancellationToken) - { - // filter out documents that are not synchronized to remote process before we attempt remote invoke: - if (!RemoteSupportedLanguages.IsSupported(document.Project.Language)) - { - return; - } - - var client = await RemoteHostClient.TryGetClientAsync(Workspace, cancellationToken).ConfigureAwait(false); - if (client == null) - { - GetLocalService().OnSourceFileUpdated(document); - return; - } - - await client.TryInvokeAsync( - document.Project.Solution, - (service, solutionInfo, cancellationToken) => service.OnSourceFileUpdatedAsync(solutionInfo, document.Id, cancellationToken), - cancellationToken).ConfigureAwait(false); - } - private sealed class LocalConnection : IDisposable { public static readonly LocalConnection Instance = new(); diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs index ba83a7954bcda..70d6028a96f96 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.EditAndContinue.Contracts; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api @@ -86,6 +87,7 @@ public async Task StartSessionAsync(Solution solution, ImmutableArray ca var newSessionId = await _encService.StartDebuggingSessionAsync( solution, new DebuggerService(capabilities), + NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments: ImmutableArray.Empty, captureAllMatchingDocuments: true, reportDiagnostics: false, diff --git a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs index f66c2c1517f25..cd5c97f3cb86b 100644 --- a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs @@ -74,6 +74,7 @@ public async Task StartSessionAsync(Solution solution, CancellationToken cancell var newSessionId = await _encService.StartDebuggingSessionAsync( solution, new DebuggerService(_capabilities), + NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments: ImmutableArray.Empty, captureAllMatchingDocuments: true, reportDiagnostics: false, diff --git a/src/Tools/ExternalAccess/Debugger/GlassTestsHotReloadService.cs b/src/Tools/ExternalAccess/Debugger/GlassTestsHotReloadService.cs index 224a63e83bbda..a68cf4ff1da6b 100644 --- a/src/Tools/ExternalAccess/Debugger/GlassTestsHotReloadService.cs +++ b/src/Tools/ExternalAccess/Debugger/GlassTestsHotReloadService.cs @@ -33,6 +33,7 @@ public async Task StartSessionAsync(Solution solution, CancellationToken cancell var newSessionId = await _encService.StartDebuggingSessionAsync( solution, new ManagedHotReloadServiceImpl(_debuggerService), + NullPdbMatchingSourceTextProvider.Instance, captureMatchingDocuments: ImmutableArray.Empty, captureAllMatchingDocuments: true, reportDiagnostics: false, diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs index e0311dc992547..959f1f9c77b4e 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs @@ -50,6 +50,21 @@ ValueTask IManagedHotReloadService.PrepareModuleForUpdateAsync(Guid moduleVersio => _callback.InvokeAsync((callback, cancellationToken) => callback.PrepareModuleForUpdateAsync(_callbackId, moduleVersionId, cancellationToken), cancellationToken); } + private sealed class SourceTextProvider : IPdbMatchingSourceTextProvider + { + private readonly RemoteCallback _callback; + private readonly RemoteServiceCallbackId _callbackId; + + public SourceTextProvider(RemoteCallback callback, RemoteServiceCallbackId callbackId) + { + _callback = callback; + _callbackId = callbackId; + } + + public ValueTask TryGetMatchingSourceTextAsync(string filePath, ImmutableArray requiredChecksum, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + => _callback.InvokeAsync((callback, cancellationToken) => callback.TryGetMatchingSourceTextAsync(_callbackId, filePath, requiredChecksum, checksumAlgorithm, cancellationToken), cancellationToken); + } + private readonly RemoteCallback _callback; public RemoteEditAndContinueService(in ServiceConstructionArguments arguments, RemoteCallback callback) @@ -72,7 +87,9 @@ public ValueTask StartDebuggingSessionAsync(Checksum solutio return RunServiceAsync(solutionChecksum, async solution => { var debuggerService = new ManagedEditAndContinueDebuggerService(_callback, callbackId); - var sessionId = await GetService().StartDebuggingSessionAsync(solution, debuggerService, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics, cancellationToken).ConfigureAwait(false); + var sourceTextProvider = new SourceTextProvider(_callback, callbackId); + + var sessionId = await GetService().StartDebuggingSessionAsync(solution, debuggerService, sourceTextProvider, captureMatchingDocuments, captureAllMatchingDocuments, reportDiagnostics, cancellationToken).ConfigureAwait(false); return sessionId; }, cancellationToken); } @@ -217,24 +234,5 @@ public ValueTask> GetAdjustedActiveStatement return await GetService().GetCurrentActiveStatementPositionAsync(sessionId, solution, CreateActiveStatementSpanProvider(callbackId), instructionId, cancellationToken).ConfigureAwait(false); }, cancellationToken); } - - /// - /// Remote API. - /// - public ValueTask OnSourceFileUpdatedAsync(Checksum solutionChecksum, DocumentId documentId, CancellationToken cancellationToken) - { - return RunServiceAsync(solutionChecksum, solution => - { - // TODO: Non-C#/VB documents are not currently serialized to remote workspace. - // https://github.com/dotnet/roslyn/issues/47341 - var document = solution.GetDocument(documentId); - if (document != null) - { - GetService().OnSourceFileUpdated(document); - } - - return ValueTaskFactory.CompletedTask; - }, cancellationToken); - } } } From 3998311481228b4ddf64977a5eff39cfdbe21bd9 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 7 Oct 2022 15:41:14 -0700 Subject: [PATCH 30/32] Fix --- .../EditAndContinue/EditAndContinueWorkspaceServiceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 285f26ab738ae..a6eca9014733c 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -409,7 +409,7 @@ internal sealed class FailingTextLoader : TextLoader internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { Assert.True(false, $"Content of document should never be loaded"); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } From 61ae4fd33a554f67ff8e3798eea91b2493641bfc Mon Sep 17 00:00:00 2001 From: tmat Date: Sun, 9 Oct 2022 10:05:44 -0700 Subject: [PATCH 31/32] Move GetSyntaxTreeFilePath to DocumentAttributes --- .../Workspace/Solution/DocumentInfo.cs | 6 ++++ .../Workspace/Solution/DocumentState.cs | 31 +++++-------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 0c3c7a94997f3..de436542b7c4a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -236,6 +236,12 @@ public DocumentAttributes With( return new DocumentAttributes(newId, newName, newFolders, newSourceCodeKind, newFilePath, newIsGenerated, newDesignTimeOnly); } + // This is the string used to represent the FilePath property on a SyntaxTree object. + // if the document does not yet have a file path, use the document's name instead in regular code + // or an empty string in script code. + public string SyntaxTreeFilePath + => FilePath ?? (SourceCodeKind == SourceCodeKind.Regular ? Name : ""); + bool IObjectWritable.ShouldReuseInSerialization => true; public void WriteTo(ObjectWriter writer) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index 5fa664df4da60..ad7a88ffc5589 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -77,7 +77,7 @@ public DocumentState( TextAndVersionSource, LoadTextOptions, info.Id.ProjectId, - GetSyntaxTreeFilePath(info.Attributes), + info.Attributes.SyntaxTreeFilePath, options, languageServices); } @@ -100,21 +100,6 @@ public SourceCodeKind SourceCodeKind public bool IsGenerated => Attributes.IsGenerated; - // This is the string used to represent the FilePath property on a SyntaxTree object. - // if the document does not yet have a file path, use the document's name instead in regular code - // or an empty string in script code. - private static string GetSyntaxTreeFilePath(DocumentInfo.DocumentAttributes info) - { - if (info.FilePath != null) - { - return info.FilePath; - } - - return info.SourceCodeKind == SourceCodeKind.Regular - ? info.Name - : ""; - } - protected static ValueSource CreateLazyFullyParsedTree( ITextAndVersionSource newTextSource, LoadTextOptions loadTextOptions, @@ -358,7 +343,7 @@ public DocumentState UpdateChecksumAlgorithm(SourceHashAlgorithm checksumAlgorit TextAndVersionSource, newLoadTextOptions, Id.ProjectId, - GetSyntaxTreeFilePath(Attributes), + Attributes.SyntaxTreeFilePath, _options, _languageServices) : null; @@ -419,7 +404,7 @@ private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocesso else if (existingTree.TryGetRoot(out var existingRoot) && !existingRoot.ContainsDirectives) { var treeFactory = _languageServices.GetRequiredService(); - newTree = treeFactory.CreateSyntaxTree(GetSyntaxTreeFilePath(Attributes), options, existingTree.Encoding, LoadTextOptions.ChecksumAlgorithm, existingRoot); + newTree = treeFactory.CreateSyntaxTree(Attributes.SyntaxTreeFilePath, options, existingTree.Encoding, LoadTextOptions.ChecksumAlgorithm, existingRoot); } if (newTree is not null) @@ -431,7 +416,7 @@ private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocesso TextAndVersionSource, LoadTextOptions, Id.ProjectId, - GetSyntaxTreeFilePath(Attributes), + Attributes.SyntaxTreeFilePath, options, _languageServices); @@ -493,7 +478,7 @@ public DocumentState UpdateFilePath(string? filePath) TextAndVersionSource, LoadTextOptions, Id.ProjectId, - GetSyntaxTreeFilePath(newAttributes), + newAttributes.SyntaxTreeFilePath, _options, _languageServices) : null; @@ -533,7 +518,7 @@ protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSou newTextSource, LoadTextOptions, Id.ProjectId, - GetSyntaxTreeFilePath(Attributes), + Attributes.SyntaxTreeFilePath, _options, _languageServices, mode); // TODO: understand why the mode is given here. If we're preserving text by identity, why also preserve the tree? @@ -655,7 +640,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT if (mode == PreservationMode.PreserveIdentity || !factory.CanCreateRecoverableTree(newRoot)) { - tree = factory.CreateSyntaxTree(GetSyntaxTreeFilePath(attributes), options, encoding, checksumAlgorithm, newRoot); + tree = factory.CreateSyntaxTree(attributes.SyntaxTreeFilePath, options, encoding, checksumAlgorithm, newRoot); // its okay to use a strong cached AsyncLazy here because the compiler layer SyntaxTree will also keep the text alive once its built. lazyTextAndVersion = new TreeTextSource( @@ -683,7 +668,7 @@ private static (ITextAndVersionSource, TreeAndVersion) CreateRecoverableTextAndT cacheResult: false)), textVersion); - tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, GetSyntaxTreeFilePath(attributes), options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); + tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, attributes.SyntaxTreeFilePath, options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); } return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion)); From 82f825e6e9973f46b16b236d88b9e8c8b077859f Mon Sep 17 00:00:00 2001 From: tmat Date: Sun, 9 Oct 2022 10:47:26 -0700 Subject: [PATCH 32/32] Test --- .../CoreTest/SolutionTests/DocumentInfoTests.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs index c63826c4f6c14..320ce9840a42c 100644 --- a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs @@ -87,13 +87,17 @@ public void Create_Folders() } [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData("path")] - public void Create_FilePath(string path) + [InlineData(SourceCodeKind.Script, null, "")] + [InlineData(SourceCodeKind.Script, "", "")] + [InlineData(SourceCodeKind.Script, "path", "path")] + [InlineData(SourceCodeKind.Regular, null, "doc_name")] + [InlineData(SourceCodeKind.Regular, "", "")] + [InlineData(SourceCodeKind.Regular, "path", "path")] + public void Create_FilePath(SourceCodeKind kind, string path, string expectedSyntaxTreeFilePath) { - var info = DocumentInfo.Create(DocumentId.CreateNewId(ProjectId.CreateNewId()), "doc", filePath: path); + var info = DocumentInfo.Create(DocumentId.CreateNewId(ProjectId.CreateNewId()), "doc_name", filePath: path, sourceCodeKind: kind); Assert.Equal(path, info.FilePath); + Assert.Equal(expectedSyntaxTreeFilePath, info.Attributes.SyntaxTreeFilePath); } [Fact]